diff options
author | David Robillard <d@drobilla.net> | 2007-05-04 03:59:38 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2007-05-04 03:59:38 +0000 |
commit | a96166710faf2447ed10194d1829db5564b0dff9 (patch) | |
tree | 4eb792fcceee17f188566fc6e6bee11e5a8c4336 /src/libs/gui | |
parent | cf14d321e8f084f742b03e09c086d5ef30297492 (diff) | |
download | ingen-a96166710faf2447ed10194d1829db5564b0dff9.tar.gz ingen-a96166710faf2447ed10194d1829db5564b0dff9.tar.bz2 ingen-a96166710faf2447ed10194d1829db5564b0dff9.zip |
Made engine, serialisation, client library, and GUI all dynamically loaded modules.
Combined all executables into a single "ingen" program which can do everything.
git-svn-id: http://svn.drobilla.net/lad/ingen@493 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/libs/gui')
76 files changed, 13468 insertions, 0 deletions
diff --git a/src/libs/gui/App.cpp b/src/libs/gui/App.cpp new file mode 100644 index 00000000..928e5f5e --- /dev/null +++ b/src/libs/gui/App.cpp @@ -0,0 +1,260 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "App.h" +#include <cassert> +#include <string> +#include <fstream> +#include <libgnomecanvasmm.h> +#include <time.h> +#include <sys/time.h> +#include <raul/Path.h> +#include "interface/EngineInterface.h" +#include "client/ObjectModel.h" +#include "client/PatchModel.h" +#include "client/Store.h" +#include "NodeModule.h" +#include "ControlPanel.h" +#include "SubpatchModule.h" +#include "LoadPluginWindow.h" +#include "PatchWindow.h" +#include "MessagesWindow.h" +#include "ConfigWindow.h" +#include "GladeFactory.h" +#include "PatchTreeWindow.h" +#include "Configuration.h" +#include "ConnectWindow.h" +#include "ThreadedLoader.h" +#include "WindowFactory.h" +#ifdef HAVE_LASH +#include "LashController.h" +#endif +using std::cerr; using std::cout; using std::endl; +using std::string; +namespace Ingen { namespace Client { class PluginModel; } } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class Port; + + +/// Singleton instance +App* App::_instance = 0; + + +App::App() +: _configuration(new Configuration()), + _about_dialog(NULL), + _window_factory(new WindowFactory()), + _enable_signal(true) +{ + Glib::RefPtr<Gnome::Glade::Xml> glade_xml = GladeFactory::new_glade_reference(); + + glade_xml->get_widget_derived("connect_win", _connect_window); + glade_xml->get_widget_derived("messages_win", _messages_window); + glade_xml->get_widget_derived("patch_tree_win", _patch_tree_window); + glade_xml->get_widget_derived("config_win", _config_window); + glade_xml->get_widget("about_win", _about_dialog); + + _rdf_world.add_prefix("xsd", "http://www.w3.org/2001/XMLSchema#"); + _rdf_world.add_prefix("ingen", "http://drobilla.net/ns/ingen#"); + _rdf_world.add_prefix("ingenuity", "http://drobilla.net/ns/ingenuity#"); + _rdf_world.add_prefix("lv2", "http://lv2plug.in/ontology#"); + _rdf_world.add_prefix("rdfs", "http://www.w3.org/2000/01/rdf-schema#"); + _rdf_world.add_prefix("doap", "http://usefulinc.com/ns/doap#"); + + _config_window->configuration(_configuration); + +#ifdef HAVE_SLV2 + SLV2World slv2_world = slv2_world_new_using_rdf_world(_rdf_world.world()); + PluginModel::set_slv2_world(slv2_world); +#endif +} + + +App::~App() +{ +} + + +void +App::run(int argc, char** argv) +{ + Gnome::Canvas::init(); + Gtk::Main main(argc, argv); + + if (!_instance) + _instance = new App(); + + /* Load settings */ + _instance->configuration()->load_settings(); + _instance->configuration()->apply_settings(); + + Gtk::Window::set_default_icon_from_file(PKGDATADIR "/ingen.svg"); + App::instance().connect_window()->start(); + + main.run(); +} + + +void +App::attach(const SharedPtr<EngineInterface>& engine, const SharedPtr<SigClientInterface>& client) +{ + assert( ! _engine); + assert( ! _client); + assert( ! _store); + assert( ! _loader); + + _engine = engine; + _client = client; + _store = SharedPtr<Store>(new Store(engine, client)); + _loader = SharedPtr<ThreadedLoader>(new ThreadedLoader(engine)); + + _patch_tree_window->init(*_store); +} + + +void +App::detach() +{ + if (_engine) { + _window_factory->clear(); + _store->clear(); + + _loader.reset(); + _store.reset(); + _client.reset(); + _engine.reset(); + } +} + + +void +App::error_message(const string& str) +{ + _messages_window->post(str); + _messages_window->show(); + _messages_window->raise(); +} + + +/* +bool +App::idle_callback() +{ + _client_hooks->process_events(); + +#ifdef HAVE_LASH + //if (lash_controller->enabled()) + // lash_controller->process_events(); +#endif + + return true; +} +*/ + + +/******** Event Handlers ************/ + + +#if 0 +App::event_load_session() +{ + Gtk::FileChooserDialog* dialog + = new Gtk::FileChooserDialog(*_main_window, "Load Session", Gtk::FILE_CHOOSER_ACTION_OPEN); + + dialog->add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + dialog->add_button(Gtk::Stock::OPEN, Gtk::RESPONSE_OK); + int result = dialog->run(); + string filename = dialog->get_filename(); + delete dialog; + + cout << result << endl; + + assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE); + + if (result == Gtk::RESPONSE_OK) + //configuration->load_session(filename); + _controller->load_session(filename); +} + + +void +App::event_save_session_as() +{ + Gtk::FileChooserDialog dialog(*_main_window, "Save Session", Gtk::FILE_CHOOSER_ACTION_SAVE); + + /* + Gtk::VBox* box = dialog.get_vbox(); + Gtk::Label warning("Warning: Recursively saving will overwrite any subpatch files \ + without confirmation."); + box->pack_start(warning, false, false, 2); + Gtk::CheckButton recursive_checkbutton("Recursively save all subpatches"); + box->pack_start(recursive_checkbutton, false, false, 0); + recursive_checkbutton.show(); + */ + dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); + + int result = dialog.run(); + //bool recursive = recursive_checkbutton.get_active(); + + assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE); + + if (result == Gtk::RESPONSE_OK) { + string filename = dialog.get_filename(); + if (filename.length() < 11 || filename.substr(filename.length()-10) != ".omsession") + filename += ".omsession"; + + bool confirm = false; + std::fstream fin; + fin.open(filename.c_str(), std::ios::in); + if (fin.is_open()) { // File exists + string msg = "File already exists! Are you sure you want to overwrite "; + msg += filename + "?"; + Gtk::MessageDialog confir_dialog(*_main_window, + msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true); + if (confir_dialog.run() == Gtk::RESPONSE_YES) + confirm = true; + else + confirm = false; + } else { // File doesn't exist + confirm = true; + } + fin.close(); + + if (confirm) { + _controller->save_session(filename); + } + } +} +#endif + + +void +App::quit() +{ + Gtk::Main::quit(); +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/libs/gui/App.h b/src/libs/gui/App.h new file mode 100644 index 00000000..504c6920 --- /dev/null +++ b/src/libs/gui/App.h @@ -0,0 +1,140 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef APP_H +#define APP_H + +#include <cassert> +#include <string> +#include <map> +#include <list> +#include <iostream> +#include <libgnomecanvasmm.h> +#include <gtkmm.h> +#include <libglademm.h> +#include <raul/RDFWorld.h> +#include <raul/SharedPtr.h> +using std::string; using std::map; using std::list; +using std::cerr; using std::endl; + +namespace Ingen { + namespace Shared { + class EngineInterface; + } + namespace Client { + class PatchModel; + class PluginModel; + class Store; + class SigClientInterface; + } +} +using namespace Ingen::Client; +using Ingen::Shared::EngineInterface; + +/** \defgroup GUI GTK GUI + */ + +namespace Ingen { +namespace GUI { + +class MessagesWindow; +class ConfigWindow; +class PatchCanvas; +class PatchTreeView; +class PatchTreeWindow; +class ConnectWindow; +class Configuration; +class ThreadedLoader; +class WindowFactory; + + +/** Singleton master class most everything is contained within. + * + * This is a horrible god-object, but it's shrinking in size as things are + * moved out. Hopefully it will go away entirely some day.. + * + * \ingroup GUI + */ +class App +{ +public: + ~App(); + + void error_message(const string& msg); + + void attach(const SharedPtr<EngineInterface>& engine, + const SharedPtr<SigClientInterface>& client); + + void detach(); + + void quit(); + + ConnectWindow* connect_window() const { return _connect_window; } + Gtk::Dialog* about_dialog() const { return _about_dialog; } + ConfigWindow* configuration_dialog() const { return _config_window; } + MessagesWindow* messages_dialog() const { return _messages_window; } + PatchTreeWindow* patch_tree() const { return _patch_tree_window; } + Configuration* configuration() const { return _configuration; } + WindowFactory* window_factory() const { return _window_factory; } + + Raul::RDF::World* rdf_world() { return &_rdf_world; } + + const SharedPtr<EngineInterface>& engine() const { return _engine; } + const SharedPtr<SigClientInterface>& client() const { return _client; } + const SharedPtr<Store>& store() const { return _store; } + const SharedPtr<ThreadedLoader>& loader() const { return _loader; } + + static inline App& instance() { assert(_instance); return *_instance; } + + static void run(int argc, char** argv); + +protected: + App(); + static App* _instance; + + static void instantiate(int argc, char** argv); + + SharedPtr<EngineInterface> _engine; + SharedPtr<SigClientInterface> _client; + SharedPtr<Store> _store; + SharedPtr<ThreadedLoader> _loader; + + Configuration* _configuration; + + ConnectWindow* _connect_window; + MessagesWindow* _messages_window; + PatchTreeWindow* _patch_tree_window; + ConfigWindow* _config_window; + Gtk::Dialog* _about_dialog; + WindowFactory* _window_factory; + + Raul::RDF::World _rdf_world; + + /** Used to avoid feedback loops with (eg) process checkbutton + * FIXME: Maybe this should be globally implemented at the Controller level, + * disable all command sending while handling events to avoid feedback + * issues with widget event callbacks? This same pattern is duplicated + * too much... */ + bool _enable_signal; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // APP_H + diff --git a/src/libs/gui/BreadCrumb.h b/src/libs/gui/BreadCrumb.h new file mode 100644 index 00000000..96464b50 --- /dev/null +++ b/src/libs/gui/BreadCrumb.h @@ -0,0 +1,81 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BREADCRUMB_H +#define BREADCRUMB_H + +#include <gtkmm.h> +#include <raul/Path.h> +#include <raul/SharedPtr.h> +#include "PatchView.h" + +namespace Ingen { +namespace GUI { + + +/** Breadcrumb button in a PatchWindow. + * + * Each Breadcrumb stores a reference to a PatchView for quick switching. + * So, the amount of allocated PatchViews at a given time is equal to the + * number of visible breadcrumbs (which is the perfect cache for GUI + * responsiveness balanced with mem consumption). + * + * \ingroup GUI + */ +class BreadCrumb : public Gtk::ToggleButton +{ +public: + BreadCrumb(const Path& path, SharedPtr<PatchView> view = SharedPtr<PatchView>()) + : _path(path) + , _view(view) + { + assert( !view || view->patch()->path() == path); + set_border_width(0); + set_path(path); + show_all(); + } + + void set_view(SharedPtr<PatchView> view) { + assert( !view || view->patch()->path() == _path); + _view = view; + } + + const Path& path() const { return _path; } + SharedPtr<PatchView> view() const { return _view; } + + void set_path(const Path& path) + { + remove(); + const string text = (path == "/") ? "/" : path.name(); + Gtk::Label* lab = manage(new Gtk::Label(text)); + lab->set_padding(0, 0); + lab->show(); + add(*lab); + + if (_view && _view->patch()->path() != path) + _view.reset(); + } + +private: + Path _path; + SharedPtr<PatchView> _view; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // BREADCRUMB_H diff --git a/src/libs/gui/BreadCrumbBox.cpp b/src/libs/gui/BreadCrumbBox.cpp new file mode 100644 index 00000000..79a0bf69 --- /dev/null +++ b/src/libs/gui/BreadCrumbBox.cpp @@ -0,0 +1,206 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "BreadCrumbBox.h" +#include "BreadCrumb.h" +namespace Ingen { +namespace GUI { + + +BreadCrumbBox::BreadCrumbBox() +: Gtk::HBox() +, _active_path("/") +, _full_path("/") +, _enable_signal(true) +{ +} + + +SharedPtr<PatchView> +BreadCrumbBox::view(const Path& path) +{ + for (std::list<BreadCrumb*>::const_iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) + if ((*i)->path() == path) + return (*i)->view(); + + return SharedPtr<PatchView>(); +} + + +/** Sets up the crumbs to display @a path. + * + * If @a path is already part of the shown path, it will be selected and the + * children preserved. + */ +void +BreadCrumbBox::build(Path path, SharedPtr<PatchView> view) +{ + bool old_enable_signal = _enable_signal; + _enable_signal = false; + + // Moving to a path we already contain, just switch the active button + if (_breadcrumbs.size() > 0 && (path.is_parent_of(_full_path) || path == _full_path)) { + + for (std::list<BreadCrumb*>::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) { + if ((*i)->path() == path) { + (*i)->set_active(true); + if (!(*i)->view()) + (*i)->set_view(view); + + // views are expensive, having two around for the same patch is a bug + assert((*i)->view() == view); + + } else { + (*i)->set_active(false); + } + } + + _active_path = path; + _enable_signal = old_enable_signal; + + + // Moving to a child of the full path, just append crumbs (preserve view cache) + } else if (_breadcrumbs.size() > 0 && (path.is_child_of(_full_path))) { + + string suffix = path.substr(_full_path.length()); + while (suffix.length() > 0) { + if (suffix[0] == '/') + suffix = suffix.substr(1); + const string name = suffix.substr(0, suffix.find("/")); + _full_path = _full_path.base() + name; + BreadCrumb* but = create_crumb(_full_path, view); + pack_start(*but, false, false, 1); + _breadcrumbs.push_back(but); + but->show(); + if (suffix.find("/") == string::npos) + break; + else + suffix = suffix.substr(suffix.find("/")+1); + } + + for (std::list<BreadCrumb*>::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) + (*i)->set_active(false); + _breadcrumbs.back()->set_active(true); + + + // Rebuild from scratch + // Getting here is bad unless absolutely necessary, since the PatchView cache is lost + } else { + + _full_path = path; + _active_path = path; + + // Empty existing breadcrumbs + for (std::list<BreadCrumb*>::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) + remove(**i); + _breadcrumbs.clear(); + + // Add root + BreadCrumb* root_but = create_crumb("/", view); + pack_start(*root_but, false, false, 1); + _breadcrumbs.push_front(root_but); + root_but->set_active(root_but->path() == _active_path); + + Path working_path = "/"; + string suffix = path.substr(1); + while (suffix.length() > 0) { + if (suffix[0] == '/') + suffix = suffix.substr(1); + const string name = suffix.substr(0, suffix.find("/")); + working_path = working_path.base() + name; + BreadCrumb* but = create_crumb(working_path, view); + pack_start(*but, false, false, 1); + _breadcrumbs.push_back(but); + but->set_active(working_path == _active_path); + but->show(); + if (suffix.find("/") == string::npos) + break; + else + suffix = suffix.substr(suffix.find("/")+1); + } + } + + _enable_signal = old_enable_signal; +} + + +/** Create a new crumb, assigning it a reference to @a view if their paths + * match, otherwise ignoring @a view. + */ +BreadCrumb* +BreadCrumbBox::create_crumb(const Path& path, + SharedPtr<PatchView> view) +{ + BreadCrumb* but = manage(new BreadCrumb(path, + (view && path == view->patch()->path()) ? view : SharedPtr<PatchView>())); + + but->signal_toggled().connect(sigc::bind(sigc::mem_fun( + this, &BreadCrumbBox::breadcrumb_clicked), but)); + + return but; +} + + +void +BreadCrumbBox::breadcrumb_clicked(BreadCrumb* crumb) +{ + if (_enable_signal) { + _enable_signal = false; + + if (!crumb->get_active()) { + // Tried to turn off the current active button, bad user, no cookie + crumb->set_active(true); + } else { + signal_patch_selected.emit(crumb->path(), crumb->view()); + if (crumb->path() != _active_path) + crumb->set_active(false); + } + _enable_signal = true; + } +} + + +void +BreadCrumbBox::object_removed(const Path& path) +{ + for (std::list<BreadCrumb*>::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) { + if ((*i)->path() == path) { + // Remove all crumbs after the removed one (inclusive) + for (std::list<BreadCrumb*>::iterator j = i; j != _breadcrumbs.end(); ) { + BreadCrumb* bc = *j; + j = _breadcrumbs.erase(j); + remove(*bc); + } + break; + } + } +} + + +void +BreadCrumbBox::object_renamed(const Path& old_path, const Path& new_path) +{ + for (std::list<BreadCrumb*>::iterator i = _breadcrumbs.begin(); i != _breadcrumbs.end(); ++i) { + if ((*i)->path() == old_path) + (*i)->set_path(new_path); + } +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/libs/gui/BreadCrumbBox.h b/src/libs/gui/BreadCrumbBox.h new file mode 100644 index 00000000..8b806f5a --- /dev/null +++ b/src/libs/gui/BreadCrumbBox.h @@ -0,0 +1,70 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef BREADCRUMBBOX_H +#define BREADCRUMBBOX_H + +#include <list> +#include <gtkmm.h> +#include <libglademm/xml.h> +#include <libglademm.h> +#include <raul/Path.h> +#include <raul/SharedPtr.h> +#include "PatchView.h" + +namespace Ingen { +namespace GUI { + +class BreadCrumb; + + +/** Collection of breadcrumb buttons forming a path. + * + * This doubles as a cache for PatchViews. + * + * \ingroup GUI + */ +class BreadCrumbBox : public Gtk::HBox +{ +public: + BreadCrumbBox(); + + SharedPtr<PatchView> view(const Path& path); + + void build(Path path, SharedPtr<PatchView> view); + + sigc::signal<void, const Path&, SharedPtr<PatchView> > signal_patch_selected; + +private: + BreadCrumb* create_crumb(const Path& path, + SharedPtr<PatchView> view = SharedPtr<PatchView>()); + + void breadcrumb_clicked(BreadCrumb* crumb); + + void object_removed(const Path& path); + void object_renamed(const Path& old_path, const Path& new_path); + + Path _active_path; + Path _full_path; + bool _enable_signal; + std::list<BreadCrumb*> _breadcrumbs; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // BREADCRUMBBOX_H diff --git a/src/libs/gui/ConfigWindow.cpp b/src/libs/gui/ConfigWindow.cpp new file mode 100644 index 00000000..c8b29f1a --- /dev/null +++ b/src/libs/gui/ConfigWindow.cpp @@ -0,0 +1,88 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <iostream> +#include <cassert> +#include <algorithm> +#include <cctype> +#include "client/NodeModel.h" +#include "ConfigWindow.h" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +ConfigWindow::ConfigWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: Gtk::Window(cobject), + _configuration(NULL) +{ + xml->get_widget("config_path_entry", _path_entry); + xml->get_widget("config_save_button", _save_button); + xml->get_widget("config_cancel_button", _cancel_button); + xml->get_widget("config_ok_button", _ok_button); + + _save_button->signal_clicked().connect( sigc::mem_fun(this, &ConfigWindow::save_clicked)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &ConfigWindow::cancel_clicked)); + _ok_button->signal_clicked().connect( sigc::mem_fun(this, &ConfigWindow::ok_clicked)); +} + + +/** Sets the state manager for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +ConfigWindow::configuration(Configuration* sm) +{ + _configuration = sm; + _path_entry->set_text(sm->patch_path()); +} + + + +///// Event Handlers ////// + + +void +ConfigWindow::save_clicked() +{ + _configuration->patch_path(_path_entry->get_text()); + _configuration->apply_settings(); + _configuration->save_settings(); +} + + +void +ConfigWindow::cancel_clicked() +{ + hide(); +} + + +void +ConfigWindow::ok_clicked() +{ + _configuration->patch_path(_path_entry->get_text()); + _configuration->apply_settings(); + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/ConfigWindow.h b/src/libs/gui/ConfigWindow.h new file mode 100644 index 00000000..d1a2cff6 --- /dev/null +++ b/src/libs/gui/ConfigWindow.h @@ -0,0 +1,65 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONFIGWINDOW_H +#define CONFIGWINDOW_H + +#include <list> +#include <libglademm/xml.h> +#include <libglademm.h> +#include <gtkmm.h> +#include "client/PluginModel.h" +#include "Configuration.h" + +using std::list; +using Ingen::Client::PluginModel; + +namespace Ingen { +namespace GUI { + + +/** 'Configuration' window. + * + * Loaded by glade as a derived object. + * + * \ingroup GUI + */ +class ConfigWindow : public Gtk::Window +{ +public: + ConfigWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void configuration(Configuration* sm); + +private: + void save_clicked(); + void cancel_clicked(); + void ok_clicked(); + + Configuration* _configuration; + + Gtk::Entry* _path_entry; + Gtk::Button* _save_button; + Gtk::Button* _cancel_button; + Gtk::Button* _ok_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONFIGWINDOW_H diff --git a/src/libs/gui/Configuration.cpp b/src/libs/gui/Configuration.cpp new file mode 100644 index 00000000..d641ea98 --- /dev/null +++ b/src/libs/gui/Configuration.cpp @@ -0,0 +1,187 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "Configuration.h" +#include <cstdlib> +#include <cassert> +#include <iostream> +#include <fstream> +#include <map> +#include "client/PortModel.h" +#include "client/PluginModel.h" +#include "client/PatchModel.h" +#include "serialisation/Loader.h" +#include "App.h" + +using std::cerr; using std::cout; using std::endl; +using std::map; using std::string; +using Ingen::Client::PatchModel; + +namespace Ingen { +namespace GUI { + +using namespace Ingen::Client; + + +Configuration::Configuration() +: _patch_path("/usr/share/ingen/patches:/usr/local/share/ingen/patches"), + _audio_port_color( 0x394f66B0), + _control_port_color(0x396639B0), + _midi_port_color( 0x663939B0) +{ +} + + +Configuration::~Configuration() +{ +} + + +/** Loads settings from the rc file. Passing no parameter will load from + * the default location. + */ +void +Configuration::load_settings(string filename) +{ + if (filename == "") + filename = string(getenv("HOME")).append("/.omgtkrc"); + + std::ifstream is; + is.open(filename.c_str(), std::ios::in); + + if ( ! is.good()) { + cout << "[Configuration] Unable to open settings file " << filename << endl; + return; + } else { + cout << "[Configuration] Loading settings from " << filename << endl; + } + + string s; + + is >> s; + if (s != "file_version") { + cerr << "[Configuration] Corrupt settings file, load aborted." << endl; + is.close(); + return; + } + + is >> s; + if (s != "1") { + cerr << "[Configuration] Unknown settings file version number, load aborted." << endl; + is.close(); + return; + } + + is >> s; + if (s != "patch_path") { + cerr << "[Configuration] Corrupt settings file, load aborted." << endl; + is.close(); + return; + } + + is >> s; + _patch_path = s; + + is.close(); +} + + +/** Saves settings to rc file. Passing no parameter will save to the + * default location. + */ +void +Configuration::save_settings(string filename) +{ + if (filename == "") + filename = string(getenv("HOME")).append("/.omgtkrc"); + + std::ofstream os; + os.open(filename.c_str(), std::ios::out); + + if ( ! os.good()) { + cout << "[Configuration] Unable to write to setting file " << filename << endl; + return; + } else { + cout << "[Configuration] Saving settings to " << filename << endl; + } + + os << "file_version 1" << endl; + os << "patch_path " << _patch_path << endl; + + os.close(); +} + + +/** Applies the current loaded settings to whichever parts of the app + * need updating. + */ +void +Configuration::apply_settings() +{ + cerr << "FIXME: patch path" << endl; + //App::instance().loader()->set_patch_path(_patch_path); +} + + +int +Configuration::get_port_color(const PortModel* pi) +{ + assert(pi != NULL); + + if (pi->is_control()) { + return _control_port_color; + } else if (pi->is_audio()) { + return _audio_port_color; + } else if (pi->is_midi()) { + return _midi_port_color; + } + + cerr << "[Configuration] Unknown port type! Port will be bright red, this is an error." << endl; + return 0xFF0000B0; +} + +/* +Coord +Configuration::get_window_location(const string& id) +{ + return _window_locations[id]; +} + + +void +Configuration::set_window_location(const string& id, Coord loc) +{ + _window_locations[id] = loc; +} + + +Coord +Configuration::get_window_size(const string& id) +{ + return _window_sizes[id]; +} + + +void +Configuration::set_window_size(const string& id, Coord size) +{ + _window_sizes[id] = size; +}*/ + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/Configuration.h b/src/libs/gui/Configuration.h new file mode 100644 index 00000000..b1a861cb --- /dev/null +++ b/src/libs/gui/Configuration.h @@ -0,0 +1,79 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#include <string> + +namespace Ingen { namespace Client { class PortModel; } } +using Ingen::Client::PortModel; +using std::string; + +struct Coord { double x; double y; }; + +namespace Ingen { +namespace GUI { + +class Controller; + + +/** Singleton state manager for the entire app. + * + * Stores settings like color preferences, search paths, etc. + * (ie any user-defined preferences to be stoed in the rc file). + * + * \ingroup GUI + */ +class Configuration +{ +public: + Configuration(); + ~Configuration(); + + void load_settings(string filename = ""); + void save_settings(string filename = ""); + + void apply_settings(); + + string patch_path() { return _patch_path; } + void patch_path(const string& path) { _patch_path = path; } + + const string& patch_folder() { return _patch_folder; } + void set_patch_folder(const string& f) { _patch_folder = f; } + + int get_port_color(const PortModel* pi); + +private: + /** Search path for patch files. Colon delimited, as usual. */ + string _patch_path; + + /** Most recent patch folder shown in open dialog */ + string _patch_folder; + + int _audio_port_color; + int _control_port_color; + int _midi_port_color; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONFIG_H + + diff --git a/src/libs/gui/ConnectWindow.cpp b/src/libs/gui/ConnectWindow.cpp new file mode 100644 index 00000000..367a38fc --- /dev/null +++ b/src/libs/gui/ConnectWindow.cpp @@ -0,0 +1,426 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string> +#include <time.h> +#include <sys/time.h> +#include <stdlib.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <raul/Process.h> +#include "config.h" +#include "interface/ClientKey.h" +#include "interface/EngineInterface.h" +#include "engine/tuning.h" +#include "engine/Engine.h" +#include "engine/DirectResponder.h" +#include "engine/QueuedEngineInterface.h" +#include "client/OSCClientReceiver.h" +#include "client/OSCEngineSender.h" +#include "client/ThreadedSigClientInterface.h" +#include "client/Store.h" +#include "client/PatchModel.h" +#include "module/Module.h" +#include "App.h" +#include "WindowFactory.h" +#include "ConnectWindow.h" +using Ingen::QueuedEngineInterface; +using Ingen::Client::ThreadedSigClientInterface; + +namespace Ingen { +namespace GUI { + + +// Paste together some interfaces to get the combination we want + + +struct OSCSigEmitter : public OSCClientReceiver, public ThreadedSigClientInterface { + OSCSigEmitter(size_t queue_size, int listen_port) + : Ingen::Shared::ClientInterface() + , OSCClientReceiver(listen_port) + , ThreadedSigClientInterface(queue_size) + { + } +}; + + +// ConnectWindow + + +ConnectWindow::ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Gtk::Dialog(cobject) + , _mode(CONNECT_REMOTE) + , _ping_id(-1) + , _attached(false) + , _connect_stage(0) + , _new_engine(NULL) +{ + xml->get_widget("connect_icon", _icon); + xml->get_widget("connect_progress_bar", _progress_bar); + xml->get_widget("connect_progress_label", _progress_label); + xml->get_widget("connect_server_radiobutton", _server_radio); + xml->get_widget("connect_url_entry", _url_entry); + xml->get_widget("connect_launch_radiobutton", _launch_radio); + xml->get_widget("connect_port_spinbutton", _port_spinbutton); + xml->get_widget("connect_internal_radiobutton", _internal_radio); + xml->get_widget("connect_disconnect_button", _disconnect_button); + xml->get_widget("connect_connect_button", _connect_button); + xml->get_widget("connect_quit_button", _quit_button); + + _server_radio->signal_toggled().connect(sigc::mem_fun(this, &ConnectWindow::server_toggled)); + _launch_radio->signal_toggled().connect(sigc::mem_fun(this, &ConnectWindow::launch_toggled)); + _internal_radio->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::internal_toggled)); + _disconnect_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::disconnect)); + _connect_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::connect)); + _quit_button->signal_clicked().connect(sigc::mem_fun(this, &ConnectWindow::quit)); + + _engine_module = Ingen::Shared::load_module("ingen_engine"); + + if (!_engine_module) { + cerr << "Unable to load ingen_engine module, internal engine unavailable." << endl; + cerr << "If you are running from the source tree, run ingenuity_dev." << endl; + } + + bool found = _engine_module->get_symbol("new_engine", (void*&)_new_engine); + + if (!found) { + cerr << "Unable to find module entry point, internal engine unavailable." << endl; + _engine_module.reset(); + } +} + + +void +ConnectWindow::start() +{ + resize(100, 100); + init(); + show(); + connect(); +} + + +void +ConnectWindow::init() +{ + _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); + _progress_bar->set_fraction(0.0); + _url_entry->set_sensitive(true); + _connect_button->set_sensitive(true); + _disconnect_button->set_sensitive(false); + _port_spinbutton->set_sensitive(false); + _launch_radio->set_sensitive(true); + if (_new_engine) + _internal_radio->set_sensitive(true); + else + _internal_radio->set_sensitive(false); + server_toggled(); + + _progress_label->set_text(string("Disconnected")); +} + + +/** Launch (if applicable) and connect to the Engine. + * + * This will create the EngineInterface and ClientInterface and initialize + * the App with them. + */ +void +ConnectWindow::connect() +{ + assert(!_attached); + assert(!App::instance().engine()); + assert(!App::instance().client()); + + _connect_button->set_sensitive(false); + _disconnect_button->set_label("gtk-cancel"); + _disconnect_button->set_sensitive(true); + + _connect_stage = 0; + + if (_mode == CONNECT_REMOTE) { + SharedPtr<EngineInterface> engine( + new OSCEngineSender(_url_entry->get_text())); + + OSCSigEmitter* ose = new OSCSigEmitter(1024, 16181); // FIXME: args + SharedPtr<SigClientInterface> client(ose); + App::instance().attach(engine, client); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &ConnectWindow::gtk_callback), 100); + + Glib::signal_timeout().connect( + sigc::mem_fun(ose, &ThreadedSigClientInterface::emit_signals), 2, G_PRIORITY_HIGH_IDLE); + + } else if (_mode == LAUNCH_REMOTE) { + + int port = _port_spinbutton->get_value_as_int(); + char port_str[6]; + snprintf(port_str, 6, "%u", port); + const string cmd = string("ingen --port=").append(port_str); + + if (Raul::Process::launch(cmd)) { + SharedPtr<EngineInterface> engine( + new OSCEngineSender(string("osc.udp://localhost:").append(port_str))); + + OSCSigEmitter* ose = new OSCSigEmitter(1024, 16181); // FIXME: args + SharedPtr<SigClientInterface> client(ose); + App::instance().attach(engine, client); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &ConnectWindow::gtk_callback), 100); + + Glib::signal_timeout().connect( + sigc::mem_fun(ose, &ThreadedSigClientInterface::emit_signals), + 2, G_PRIORITY_HIGH_IDLE); + } else { + cerr << "Failed to launch ingen process." << endl; + } + + } else if (_mode == INTERNAL) { + assert(_new_engine); + SharedPtr<Ingen::Engine> engine(_new_engine()); + + engine->start_jack_driver(); + + SharedPtr<Ingen::EngineInterface> engine_interface = engine->new_queued_interface(); + + ThreadedSigClientInterface* tsci = new ThreadedSigClientInterface(Ingen::event_queue_size); + SharedPtr<SigClientInterface> client(tsci); + + App::instance().attach(engine_interface, client); + + engine_interface->set_responder(SharedPtr<Ingen::Shared::Responder>(new Ingen::DirectResponder(client, 1))); + + //engine->set_event_source(engine_interface); + + engine->activate(); + + Glib::signal_timeout().connect( + sigc::mem_fun(engine.get(), &Ingen::Engine::main_iteration), 1000); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &ConnectWindow::gtk_callback), 100); + + Glib::signal_timeout().connect( + sigc::mem_fun(tsci, &ThreadedSigClientInterface::emit_signals), 2, G_PRIORITY_HIGH_IDLE); + } +} + + +void +ConnectWindow::disconnect() +{ + _connect_stage = -1; + _attached = false; + + _progress_bar->set_fraction(0.0); + _connect_button->set_sensitive(false); + _disconnect_button->set_sensitive(false); + + App::instance().detach(); + + init(); + + _connect_button->set_sensitive(true); + _disconnect_button->set_sensitive(false); +} + + +void +ConnectWindow::quit() +{ + if (_attached) { + Gtk::MessageDialog d(*this, "This will exit the GUI, but the engine will " + "remain running (if it is remote).\n\nAre you sure you want to quit?", + true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true); + d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE); + int ret = d.run(); + if (ret == Gtk::RESPONSE_CLOSE) + Gtk::Main::quit(); + } else { + Gtk::Main::quit(); + } +} + + +void +ConnectWindow::server_toggled() +{ + _url_entry->set_sensitive(true); + _port_spinbutton->set_sensitive(false); + _mode = CONNECT_REMOTE; +} + + +void +ConnectWindow::launch_toggled() +{ + _url_entry->set_sensitive(false); + _port_spinbutton->set_sensitive(true); + _mode = LAUNCH_REMOTE; +} + + +void +ConnectWindow::internal_toggled() +{ + _url_entry->set_sensitive(false); + _port_spinbutton->set_sensitive(false); + _mode = INTERNAL; +} + + +bool +ConnectWindow::gtk_callback() +{ + /* This isn't very nice (isn't threaded), but better than no dialog at + * all like before :) + */ + + // Timing stuff for repeated attach attempts + timeval now; + gettimeofday(&now, NULL); + static timeval last = now; + + /* Connecting to engine */ + if (_connect_stage == 0) { + + assert(!_attached); + assert(App::instance().engine()); + assert(App::instance().client()); + + // FIXME + //assert(!App::instance().engine()->is_attached()); + _progress_label->set_text("Connecting to engine..."); + present(); + + App::instance().client()->response_sig.connect(sigc::mem_fun(this, &ConnectWindow::response_received)); + + _ping_id = rand(); + while (_ping_id == -1) + _ping_id = rand(); + + App::instance().engine()->set_next_response_id(_ping_id); + App::instance().engine()->ping(); + ++_connect_stage; + + + } else if (_connect_stage == 1) { + if (_attached) { + App::instance().engine()->activate(); + ++_connect_stage; + } else { + const float ms_since_last = (now.tv_sec - last.tv_sec) * 1000.0f + + (now.tv_usec - last.tv_usec) * 0.001f; + if (ms_since_last > 1000) { + App::instance().engine()->set_next_response_id(_ping_id); + App::instance().engine()->ping(); + last = now; + } + } + } else if (_connect_stage == 2) { + _progress_label->set_text(string("Registering as client...")); + //App::instance().engine()->register_client(App::instance().engine()->client_hooks()); + // FIXME + //auto_ptr<ClientInterface> client(new ThreadedSigClientInterface(); + App::instance().engine()->register_client(ClientKey(), App::instance().client()); + App::instance().engine()->load_plugins(); + ++_connect_stage; + } else if (_connect_stage == 3) { + // Register idle callback to process events and whatnot + // (Gtk refreshes at priority G_PRIORITY_HIGH_IDLE+20) + /*Glib::signal_timeout().connect( + sigc::mem_fun(this, &App::idle_callback), 100, G_PRIORITY_HIGH_IDLE);*/ + //Glib::signal_idle().connect(sigc::mem_fun(this, &App::idle_callback)); + /* Glib::signal_timeout().connect( + sigc::mem_fun((ThreadedSigClientInterface*)_client, &ThreadedSigClientInterface::emit_signals), + 5, G_PRIORITY_DEFAULT_IDLE);*/ + + _progress_label->set_text(string("Requesting plugins...")); + App::instance().engine()->request_plugins(); + ++_connect_stage; + } else if (_connect_stage == 4) { + // Wait for first plugins message + if (App::instance().store()->plugins().size() > 0) { + _progress_label->set_text(string("Receiving plugins...")); + ++_connect_stage; + } + } else if (_connect_stage == 5) { + // FIXME + /*if (App::instance().store().plugins().size() < _client->num_plugins()) { + static char buf[32]; + snprintf(buf, 32, "%zu/%zu", App::instance().store().plugins().size(), + ThreadedSigClientInterface::instance()->num_plugins()); + _progress_bar->set_text(Glib::ustring(buf)); + _progress_bar->set_fraction( + App::instance().store().plugins().size() / (double)_client->num_plugins()); + } else {*/ + _progress_bar->set_text(""); + ++_connect_stage; + //} + } else if (_connect_stage == 6) { + _progress_label->set_text(string("Waiting for root patch...")); + App::instance().engine()->request_all_objects(); + ++_connect_stage; + } else if (_connect_stage == 7) { + if (App::instance().store()->objects().size() > 0) { + SharedPtr<PatchModel> root = PtrCast<PatchModel>(App::instance().store()->object("/")); + assert(root); + App::instance().window_factory()->present_patch(root); + ++_connect_stage; + } + } else if (_connect_stage == 8) { + _progress_label->set_text("Connected to engine"); + ++_connect_stage; + } else if (_connect_stage == 9) { + ++_connect_stage; + hide(); + } else if (_connect_stage == 10) { + _icon->set(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); + _progress_bar->set_fraction(1.0); + _url_entry->set_sensitive(false); + _connect_button->set_sensitive(false); + _disconnect_button->set_label("gtk-disconnect"); + _disconnect_button->set_sensitive(true); + _port_spinbutton->set_sensitive(false); + _launch_radio->set_sensitive(false); + _internal_radio->set_sensitive(false); + _connect_stage = 0; // set ourselves up for next time (if there is one) + return false; // deregister this callback + } + + if (_connect_stage != 5) // yeah, ew + _progress_bar->pulse(); + + if (_connect_stage == -1) { // we were cancelled + _icon->set(Gtk::Stock::DISCONNECT, Gtk::ICON_SIZE_LARGE_TOOLBAR); + _progress_bar->set_fraction(0.0); + _connect_button->set_sensitive(true); + _disconnect_button->set_sensitive(false); + _disconnect_button->set_label("gtk-disconnect"); + _progress_label->set_text(string("Disconnected")); + return false; + } else { + return true; + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/ConnectWindow.h b/src/libs/gui/ConnectWindow.h new file mode 100644 index 00000000..1a4a654e --- /dev/null +++ b/src/libs/gui/ConnectWindow.h @@ -0,0 +1,92 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONNECT_WINDOW_H +#define CONNECT_WINDOW_H + +#include <gtkmm.h> +#include <libglademm/xml.h> +#include <libglademm.h> +#include <raul/SharedPtr.h> +#include "client/ThreadedSigClientInterface.h" +using Ingen::Client::SigClientInterface; + +namespace Ingen { class Engine; class QueuedEngineInterface; } + +namespace Ingen { +namespace GUI { + +class App; +class Controller; + + +/** The initially visible "Connect to engine" window. + * + * This handles actually connecting to the engine and making sure everything + * is ready before really launching the app (eg wait for the root patch). + * + * \ingroup GUI + */ +class ConnectWindow : public Gtk::Dialog +{ +public: + ConnectWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void start(); + void response_received(int32_t id, bool, string) { if ((id) == _ping_id) _attached = true; } + +private: + enum Mode { CONNECT_REMOTE, LAUNCH_REMOTE, INTERNAL }; + + void server_toggled(); + void launch_toggled(); + void internal_toggled(); + + void init(); + void disconnect(); + void connect(); + void quit(); + + bool gtk_callback(); + + Mode _mode; + int32_t _ping_id; + bool _attached; + + int _connect_stage; + + SharedPtr<Glib::Module> _engine_module; + Ingen::Engine* (*_new_engine)(); + + Gtk::Image* _icon; + Gtk::ProgressBar* _progress_bar; + Gtk::Label* _progress_label; + Gtk::Entry* _url_entry; + Gtk::RadioButton* _server_radio; + Gtk::SpinButton* _port_spinbutton; + Gtk::RadioButton* _launch_radio; + Gtk::RadioButton* _internal_radio; + Gtk::Button* _disconnect_button; + Gtk::Button* _connect_button; + Gtk::Button* _quit_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONNECT_WINDOW_H diff --git a/src/libs/gui/Connection.h b/src/libs/gui/Connection.h new file mode 100644 index 00000000..d4d5c10c --- /dev/null +++ b/src/libs/gui/Connection.h @@ -0,0 +1,60 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONNECTION_H +#define CONNECTION_H + +#include <cassert> +#include <string> +#include <flowcanvas/Connection.h> +#include <raul/SharedPtr.h> +#include "client/ConnectionModel.h" +using Ingen::Client::ConnectionModel; + +namespace Ingen { +namespace GUI { + + +/** A Connection in a Patch. + * + * \ingroup GUI + */ +class Connection : public LibFlowCanvas::Connection +{ +public: + Connection(boost::shared_ptr<LibFlowCanvas::FlowCanvas> canvas, + boost::shared_ptr<ConnectionModel> model, + boost::shared_ptr<LibFlowCanvas::Connectable> src, + boost::shared_ptr<LibFlowCanvas::Connectable> dst, + uint32_t color) + : LibFlowCanvas::Connection(canvas, src, dst, color) + , _connection_model(model) + {} + + virtual ~Connection() {} + + SharedPtr<ConnectionModel> model() const { return _connection_model; } + +private: + SharedPtr<ConnectionModel> _connection_model; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONNECTION_H diff --git a/src/libs/gui/ControlGroups.cpp b/src/libs/gui/ControlGroups.cpp new file mode 100644 index 00000000..7af9011b --- /dev/null +++ b/src/libs/gui/ControlGroups.cpp @@ -0,0 +1,430 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cmath> +#include <iostream> +#include "interface/EngineInterface.h" +#include "client/PluginModel.h" +#include "client/NodeModel.h" +#include "client/PortModel.h" +#include "ControlGroups.h" +#include "ControlPanel.h" +#include "PortPropertiesWindow.h" +#include "GladeFactory.h" +#include "App.h" + +using std::cerr; using std::cout; using std::endl; + +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + + +// ////////////////////// ControlGroup ///////////////////////////////// // + +ControlGroup::ControlGroup(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml) +: Gtk::VBox(cobject), + _control_panel(NULL), + _enable_signal(false) +{ +} + + +void +ControlGroup::init(ControlPanel* panel, SharedPtr<PortModel> pm) +{ + _control_panel = panel; + _port_model = pm, + + assert(_port_model); + assert(panel); + + pm->control_change_sig.connect(sigc::mem_fun(this, &ControlGroup::set_value)); +} + + +// ////////////////// SliderControlGroup ////////////////////// // + + +SliderControlGroup::SliderControlGroup(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: ControlGroup(cobject, xml), + _enabled(true) +{ + xml->get_widget("control_strip_name_label", _name_label); + xml->get_widget("control_strip_slider", _slider); + xml->get_widget("control_strip_spinner", _value_spinner); + + Glib::RefPtr<Gnome::Glade::Xml> menu_xml = GladeFactory::new_glade_reference("port_control_menu"); + menu_xml->get_widget("port_control_menu", _menu); + menu_xml->get_widget("port_control_menu_properties", _menu_properties); + + _menu_properties->signal_activate().connect( + sigc::mem_fun(this, &SliderControlGroup::menu_properties)); +} + + +void +SliderControlGroup::init(ControlPanel* panel, SharedPtr<PortModel> pm) +{ + _enable_signal = false; + _enabled = true; + + ControlGroup::init(panel, pm); + + assert(_name_label); + assert(_slider); + + set_name(pm->path().name()); + + _slider->set_draw_value(false); + + _name_label->signal_button_press_event().connect(sigc::mem_fun(*this, &SliderControlGroup::clicked)); + _slider->signal_button_press_event().connect(sigc::mem_fun(*this, &SliderControlGroup::clicked)); + + _slider->signal_event().connect( + sigc::mem_fun(*this, &SliderControlGroup::slider_pressed)); + + _slider->signal_value_changed().connect( + sigc::mem_fun(*this, &SliderControlGroup::update_value_from_slider)); + + _value_spinner->signal_value_changed().connect( + sigc::mem_fun(*this, &SliderControlGroup::update_value_from_spinner)); + + // FIXME: code duplication w/ PortPropertiesWindow.cpp + float min = 0.0f; + float max = 1.0f; + + const Atom& min_atom = pm->get_metadata("ingen:minimum"); + const Atom& max_atom = pm->get_metadata("ingen:maximum"); + if (min_atom.type() == Atom::FLOAT && max_atom.type() == Atom::FLOAT) { + min = min_atom.get_float(); + max = max_atom.get_float(); + } + + const SharedPtr<NodeModel> parent = PtrCast<NodeModel>(pm->parent()); + + if (parent && parent->plugin() && parent->plugin()->type() == PluginModel::LV2) { + min = slv2_port_get_minimum_value( + parent->plugin()->slv2_plugin(), + slv2_plugin_get_port_by_symbol(parent->plugin()->slv2_plugin(), + pm->path().name().c_str())); + max = slv2_port_get_maximum_value( + parent->plugin()->slv2_plugin(), + slv2_plugin_get_port_by_symbol(parent->plugin()->slv2_plugin(), + pm->path().name().c_str())); + } + + if (max <= min) + max = min + 1.0f; + + _slider->set_increments(0, 0); + _slider->set_range(min, max); + _value_spinner->set_range(min, max); + + set_value(pm->value()); + + _enable_signal = true; + + show_all(); +} + + +bool +SliderControlGroup::clicked(GdkEventButton* ev) +{ + if (ev->button == 3) { + _menu->popup(ev->button, ev->time); + return true; + } else { + return false; + } +} + + +void +SliderControlGroup::menu_properties() +{ + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference(); + + PortPropertiesWindow* dialog; + xml->get_widget_derived("port_properties_win", dialog); + dialog->init(this, _port_model); + dialog->run(); +} + + +void +SliderControlGroup::set_value(float val) +{ + _enable_signal = false; + if (_enabled) { + if (_slider->get_value() != val) + _slider->set_value(val); + if (_value_spinner->get_value() != val) + _value_spinner->set_value(val); + } + _enable_signal = true; +} + + +void +SliderControlGroup::set_range(float min, float max) +{ + _slider->set_range(min, max); + _value_spinner->set_range(min, max); +} + + +void +SliderControlGroup::set_name(const string& name) +{ + string name_label = "<span weight=\"bold\">"; + name_label += name + "</span>"; + _name_label->set_markup(name_label); +} + + +void +SliderControlGroup::enable() +{ + _slider->property_sensitive() = true; + //_min_spinner->property_sensitive() = true; + //_max_spinner->property_sensitive() = true; + _value_spinner->property_sensitive() = true; + _name_label->property_sensitive() = true; +} + + +void +SliderControlGroup::disable() +{ + _slider->property_sensitive() = false; + //_min_spinner->property_sensitive() = false; + //_max_spinner->property_sensitive() = false; + _value_spinner->property_sensitive() = false; + _name_label->property_sensitive() = false; +} + + +void +SliderControlGroup::update_value_from_slider() +{ + if (_enable_signal) { + const float value = _slider->get_value(); + _enable_signal = false; + + _value_spinner->set_value(value); + _control_panel->value_changed(_port_model, value); + + _enable_signal = true; + } +} + + +void +SliderControlGroup::update_value_from_spinner() +{ + if (_enable_signal) { + _enable_signal = false; + const float value = _value_spinner->get_value(); + + /*if (value < _min_spinner->get_value()) { + _min_spinner->set_value(value); + _slider->set_range(_min_spinner->get_value(), _max_spinner->get_value()); + } + if (value > _max_spinner->get_value()) { + _max_spinner->set_value(value); + _slider->set_range(_min_spinner->get_value(), _max_spinner->get_value()); + }*/ + + _slider->set_value(value); + + _control_panel->value_changed(_port_model, value); + + //m_port_model->value(value); + _enable_signal = true; + } +} + + +/** Callback for when slider is grabbed so we can ignore set_control + * events for this port (and avoid nasty feedback issues). + */ +bool +SliderControlGroup::slider_pressed(GdkEvent* ev) +{ + //cerr << "Pressed: " << ev->type << endl; + if (ev->type == GDK_BUTTON_PRESS) { + _enabled = false; + //GtkClientInterface::instance()->set_ignore_port(_port_model->path()); + } else if (ev->type == GDK_BUTTON_RELEASE) { + _enabled = true; + //GtkClientInterface::instance()->clear_ignore_port(); + } + + return false; +} + + +// ///////////// IntegerControlGroup ////////////// // + +#if 0 +IntegerControlGroup::IntegerControlGroup(ControlPanel* panel, SharedPtr<PortModel> pm) +: ControlGroup(panel, pm), + _enable_signal(false), + _alignment(0.5, 0.5, 0.0, 0.0), + _name_label(pm->path().name()), + _spinner(1.0, 0) +{ + set_name(pm->path().name()); + + _spinner.set_range(-99999, 99999); + _spinner.set_value(_port_model->value()); + _spinner.signal_value_changed().connect( + sigc::mem_fun(*this, &IntegerControlGroup::update_value)); + _spinner.set_increments(1, 10); + + _alignment.add(_spinner); + pack_start(_name_label); + pack_start(_alignment); + + _enable_signal = true; + + show_all(); +} + + +void +IntegerControlGroup::set_name(const string& name) +{ + string name_label = "<span weight=\"bold\">"; + name_label += name + "</span>"; + _name_label->set_markup(name_label); +} + + +void +IntegerControlGroup::set_value(float val) +{ + //cerr << "[IntegerControlGroup] Setting value to " << val << endl; + _enable_signal = false; + _spinner.set_value(val); + _enable_signal = true; +} + + +void +IntegerControlGroup::enable() +{ + _spinner.property_sensitive() = true; + _name_label->property_sensitive() = true; +} + + +void +IntegerControlGroup::disable() +{ + _spinner.property_sensitive() = false; + _name_label->property_sensitive() = false; +} + + +void +IntegerControlGroup::update_value() +{ + if (_enable_signal) { + float value = _spinner.get_value(); + _control_panel->value_changed(_port_model, value); + //m_port_model->value(value); + } +} + + +// ///////////// ToggleControlGroup ////////////// // + + +ToggleControlGroup::ToggleControlGroup(ControlPanel* panel, SharedPtr<PortModel> pm) +: ControlGroup(panel, pm), + _enable_signal(false), + _alignment(0.5, 0.5, 0.0, 0.0), + _name_label(pm->path().name()) +{ + set_name(pm->path().name()); + + set_value(_port_model->value()); + _checkbutton.signal_toggled().connect( + sigc::mem_fun(*this, &ToggleControlGroup::update_value)); + + _alignment.add(_checkbutton); + pack_start(_name_label); + pack_start(_alignment); + + _enable_signal = true; + + show_all(); +} + + +void +ToggleControlGroup::set_name(const string& name) +{ + string name_label = "<span weight=\"bold\">"; + name_label += name + "</span>"; + _name_label->set_markup(name_label); +} + + +void +ToggleControlGroup::set_value(float val) +{ + //cerr << "[ToggleControlGroup] Setting value to " << val << endl; + _enable_signal = false; + _checkbutton.set_active( (val > 0.0f) ); + _enable_signal = true; +} + + +void +ToggleControlGroup::enable() +{ + _checkbutton.property_sensitive() = true; + _name_label->property_sensitive() = true; +} + + +void +ToggleControlGroup::disable() +{ + _checkbutton.property_sensitive() = false; + _name_label->property_sensitive() = false; +} + + +void +ToggleControlGroup::update_value() +{ + if (_enable_signal) { + float value = _checkbutton.get_active() ? 1.0f : 0.0f; + _control_panel->value_changed(_port_model, value); + //m_port_model->value(value); + } +} +#endif + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/ControlGroups.h b/src/libs/gui/ControlGroups.h new file mode 100644 index 00000000..4a1f758e --- /dev/null +++ b/src/libs/gui/ControlGroups.h @@ -0,0 +1,162 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONTROLGROUPS_H +#define CONTROLGROUPS_H + +#include <cassert> +#include <gtkmm.h> +#include <libglademm/xml.h> +#include <libglademm.h> +#include "client/PortModel.h" +#include <raul/SharedPtr.h> + +namespace Ingen { namespace Client { class PortModel; } } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class ControlPanel; +class PortPropertiesWindow; + + +/** A group of controls (for a single Port) in a NodeControlWindow. + * + * \ingroup GUI + */ +class ControlGroup : public Gtk::VBox +{ +public: + ControlGroup(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml); + virtual ~ControlGroup() { } + + void init(ControlPanel* panel, SharedPtr<PortModel> pm); + + inline const SharedPtr<PortModel> port_model() const { return _port_model; } + +protected: + friend class PortPropertiesWindow; + + virtual void set_value(float value) = 0; + virtual void set_range(float min, float max) {} + + ControlPanel* _control_panel; + SharedPtr<PortModel> _port_model; + bool _enable_signal; +}; + + +/** A slider for a continuous control. + * + * \ingroup GUI + */ +class SliderControlGroup : public ControlGroup +{ +public: + SliderControlGroup(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml); + void init(ControlPanel* panel, SharedPtr<PortModel> pm); + + void enable(); + void disable(); + + void set_min(float val); + void set_max(float val); + +private: + void set_name(const string& name); + + bool clicked(GdkEventButton* ev); + + void set_value(float value); + void set_range(float min, float max); + + void update_range(); + void update_value_from_slider(); + void update_value_from_spinner(); + + void menu_properties(); + + bool slider_pressed(GdkEvent* ev); + + bool _enabled; + + Gtk::Label* _name_label; + Gtk::SpinButton* _value_spinner; + Gtk::HScale* _slider; + + Gtk::Menu* _menu; + Gtk::MenuItem* _menu_properties; +}; + + +#if 0 + +/** A spinbutton for integer controls. + * + * \ingroup GUI + */ +class IntegerControlGroup : public ControlGroup +{ +public: + IntegerControlGroup(ControlPanel* panel, SharedPtr<PortModel> pm); + + void enable(); + void disable(); + +private: + void set_name(const string& name); + void set_value(float val); + + void update_value(); + + bool _enable_signal; + Gtk::Alignment _alignment; + Gtk::Label _name_label; + Gtk::SpinButton _spinner; +}; + + +/** A radio button for toggle controls. + * + * \ingroup GUI + */ +class ToggleControlGroup : public ControlGroup +{ +public: + ToggleControlGroup(ControlPanel* panel, SharedPtr<PortModel> pm); + + void enable(); + void disable(); + +private: + void set_name(const string& name); + void set_value(float val); + + void update_value(); + + bool _enable_signal; + Gtk::Alignment _alignment; + Gtk::Label _name_label; + Gtk::CheckButton _checkbutton; +}; +#endif + +} // namespace GUI +} // namespace Ingen + +#endif // CONTROLGROUPS_H diff --git a/src/libs/gui/ControlPanel.cpp b/src/libs/gui/ControlPanel.cpp new file mode 100644 index 00000000..6f3638a8 --- /dev/null +++ b/src/libs/gui/ControlPanel.cpp @@ -0,0 +1,259 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License alongCont + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "interface/EngineInterface.h" +#include "client/PatchModel.h" +#include "client/NodeModel.h" +#include "client/PortModel.h" +#include "client/PluginModel.h" +#include "App.h" +#include "ControlPanel.h" +#include "ControlGroups.h" +#include "GladeFactory.h" + +namespace Ingen { +namespace GUI { + + +ControlPanel::ControlPanel(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: Gtk::HBox(cobject), + _callback_enabled(true) +{ + xml->get_widget("control_panel_controls_box", _control_box); + xml->get_widget("control_panel_voice_controls_box", _voice_control_box); + xml->get_widget("control_panel_all_voices_radio", _all_voices_radio); + xml->get_widget("control_panel_specific_voice_radio", _specific_voice_radio); + xml->get_widget("control_panel_voice_spinbutton", _voice_spinbutton); + + _all_voices_radio->signal_toggled().connect(sigc::mem_fun(this, &ControlPanel::all_voices_selected)); + _specific_voice_radio->signal_toggled().connect(sigc::mem_fun(this, &ControlPanel::specific_voice_selected)); + _voice_spinbutton->signal_value_changed().connect(sigc::mem_fun(this, &ControlPanel::voice_selected)); + + show_all(); +} + + +ControlPanel::~ControlPanel() +{ + for (vector<ControlGroup*>::iterator i = _controls.begin(); i != _controls.end(); ++i) + delete (*i); +} + + +void +ControlPanel::init(SharedPtr<NodeModel> node, size_t poly) +{ + assert(node != NULL); + assert(poly > 0); + + if (poly > 1) { + _voice_spinbutton->set_range(0, poly - 1); + } else { + remove(*_voice_control_box); + } + + for (PortModelList::const_iterator i = node->ports().begin(); i != node->ports().end(); ++i) { + add_port(*i); + } + + _callback_enabled = true; +} + + +ControlGroup* +ControlPanel::find_port(const Path& path) const +{ + for (vector<ControlGroup*>::const_iterator cg = _controls.begin(); cg != _controls.end(); ++cg) + if ((*cg)->port_model()->path() == path) + return (*cg); + + return NULL; +} + + +/** Add a control to the panel for the given port. + */ +void +ControlPanel::add_port(SharedPtr<PortModel> pm) +{ + assert(pm); + + // Already have port, don't add another + if (find_port(pm->path()) != NULL) + return; + + // Add port + if (pm->is_control() && pm->is_input()) { + SliderControlGroup* cg = NULL; + if (pm->is_integer()) + cerr << "FIXME: integer\n"; + //cg = new IntegerControlGroup(this, pm); + else if (pm->is_toggle()) + cerr << "FIXME: toggle\n"; + //cg = new ToggleControlGroup(this, pm); + else { + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("control_strip"); + xml->get_widget_derived("control_strip", cg); + cg->init(this, pm); + } + + if (_controls.size() > 0) + _control_box->pack_start(*Gtk::manage(new Gtk::HSeparator()), false, false, 4); + + _controls.push_back(cg); + _control_box->pack_start(*cg, false, false, 0); + + cg->enable(); + /*if (pm->connected()) + cg->disable(); + else + cg->enable();*/ + + cg->show(); + + cerr << "FIXME: control panel connected port tracking\n"; + // pm->connection_sig.connect(bind(sigc::mem_fun(this, &ControlPanel::connection), pm)) + // pm->disconnection_sig.connect(bind(sigc::mem_fun(this, &ControlPanel::disconnection), pm)) + } + + Gtk::Requisition controls_size; + _control_box->size_request(controls_size); + _ideal_size.first = controls_size.width; + _ideal_size.second = controls_size.height; + + Gtk::Requisition voice_size; + _voice_control_box->size_request(voice_size); + _ideal_size.first += voice_size.width; + _ideal_size.second += voice_size.height; + + //cerr << "Setting ideal size to " << _ideal_size.first + // << " x " << _ideal_size.second << endl; +} + + +/** Remove the control for the given port. + */ +void +ControlPanel::remove_port(const Path& path) +{ + bool was_first = false; + for (vector<ControlGroup*>::iterator cg = _controls.begin(); cg != _controls.end(); ++cg) { + if ((*cg)->port_model()->path() == path) { + _control_box->remove(**cg); + if (cg == _controls.begin()) + was_first = true; + _controls.erase(cg); + break; + } + } +} + + +/** Rename the control for the given port. + */ +/* +void +ControlPanel::rename_port(const Path& old_path, const Path& new_path) +{ + for (vector<ControlGroup*>::iterator cg = _controls.begin(); cg != _controls.end(); ++cg) { + if ((*cg)->port_model()->path() == old_path) { + (*cg)->set_name(new_path.name()); + return; + } + } +} +*/ + +#if 0 +/** Enable the control for the given port. + * + * Used when all connections to port are un-made. + */ +void +ControlPanel::enable_port(const Path& path) +{ + for (vector<ControlGroup*>::iterator i = _controls.begin(); i != _controls.end(); ++i) { + if ((*i)->port_model()->path() == path) { + (*i)->enable(); + return; + } + } +} + + +/** Disable the control for the given port. + * + * Used when port is connected. + */ +void +ControlPanel::disable_port(const Path& path) +{ + for (vector<ControlGroup*>::iterator i = _controls.begin(); i != _controls.end(); ++i) { + if ((*i)->port_model()->path() == path) { + (*i)->disable(); + return; + } + } +} +#endif + +/** Callback for ControlGroups to notify this of a change. + */ +void +ControlPanel::value_changed(SharedPtr<PortModel> port, float val) +{ + if (_callback_enabled) { + App::instance().engine()->disable_responses(); + + /* Send the message, but set the client-side model's value to the new + * setting right away (so the value doesn't need to be echoed back) */ + + if (_all_voices_radio->get_active()) { + App::instance().engine()->set_port_value(port->path(), val); + port->value(val); + } else { + int voice = _voice_spinbutton->get_value_as_int(); + App::instance().engine()->set_port_value(port->path(), voice, val); + port->value(val); + } + + App::instance().engine()->set_next_response_id(rand()); // FIXME: inefficient, probably not good + } +} + +void +ControlPanel::all_voices_selected() +{ + _voice_spinbutton->property_sensitive() = false; +} + + +void +ControlPanel::specific_voice_selected() +{ + _voice_spinbutton->property_sensitive() = true; +} + + +void +ControlPanel::voice_selected() +{ +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/ControlPanel.h b/src/libs/gui/ControlPanel.h new file mode 100644 index 00000000..230b9b65 --- /dev/null +++ b/src/libs/gui/ControlPanel.h @@ -0,0 +1,94 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef CONTROLPANEL_H +#define CONTROLPANEL_H + +#include <vector> +#include <string> +#include <iostream> +#include <utility> // for pair<> +#include <sigc++/sigc++.h> +#include <gtkmm.h> +#include <libglademm/xml.h> +#include <libglademm.h> +#include <raul/Path.h> +#include "ControlGroups.h" + + +using std::vector; using std::string; using std::pair; +using std::cerr; using std::cout; using std::endl; + +namespace Ingen { namespace Client { + class PortModel; + class NodeModel; +} } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + + +/** A group of controls for a node (or patch). + * + * Used by both NodeControlWindow and the main window (for patch controls). + * + * \ingroup GUI + */ +class ControlPanel : public Gtk::HBox { +public: + ControlPanel(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml); + virtual ~ControlPanel(); + + void init(SharedPtr<NodeModel> node, size_t poly); + + ControlGroup* find_port(const Path& path) const; + + void add_port(SharedPtr<PortModel> port); + void remove_port(const Path& path); + + void enable_port(const Path& path); + void disable_port(const Path& path); + + size_t num_controls() const { return _controls.size(); } + pair<int,int> ideal_size() const { return _ideal_size; } + + // Callback for ControlGroup + void value_changed(SharedPtr<PortModel> port_path, float val); + +private: + void all_voices_selected(); + void specific_voice_selected(); + void voice_selected(); + + bool _callback_enabled; + + pair<int,int> _ideal_size; + + vector<ControlGroup*> _controls; + Gtk::VBox* _control_box; + Gtk::Box* _voice_control_box; + Gtk::RadioButton* _all_voices_radio; + Gtk::RadioButton* _specific_voice_radio; + Gtk::SpinButton* _voice_spinbutton; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // CONTROLPANEL_H diff --git a/src/libs/gui/DSSIController.cpp b/src/libs/gui/DSSIController.cpp new file mode 100644 index 00000000..f245d61f --- /dev/null +++ b/src/libs/gui/DSSIController.cpp @@ -0,0 +1,279 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "DSSIController.h" +#include <iomanip> +#include <sstream> +#include <dirent.h> +#include <unistd.h> +#include <sys/stat.h> +#include "interface/EngineInterface.h" +#include "client/NodeModel.h" +#include "App.h" +#include "DSSIModule.h" + +namespace Ingen { +namespace GUI { + + +DSSIController::DSSIController(SharedPtr<NodeModel> model) +: _banks_dirty(true) +{ +#if 0 + assert(model->plugin()->type() == PluginModel::DSSI); + Gtk::Menu::MenuList& items = _menu.items(); + items[0].property_sensitive() = true; // "Show Control Window" item + + Gtk::Menu_Helpers::MenuElem program_elem("Select Program", _program_menu); + items.push_front(program_elem); + _program_menu_item = program_elem.get_child(); + _program_menu_item->set_sensitive(false); + + items.push_front(Gtk::Menu_Helpers::MenuElem("Show Plugin GUI", + sigc::mem_fun(this, &DSSIController::show_gui))); +#endif +} + +void +DSSIController::program_add(int bank, int program, const string& name) +{ + cerr << "FIXME: DSSI add program\n"; + //node_model()->add_program(bank, program, name); + //m_banks_dirty = true; +} + + +void +DSSIController::program_remove(int bank, int program) +{ + cerr << "FIXME: DSSI add program\n"; + //node_model()->remove_program(bank, program); + //m_banks_dirty = true; +} + +/** Trivial wrapper of attempt_to_show_gui for libsigc + */ +void +DSSIController::show_gui() +{ + attempt_to_show_gui(); +} + + +void +DSSIController::update_program_menu() +{ + cerr << "FIXME: Program menu\n"; +#if 0 + _program_menu.items().clear(); + + const map<int, map<int, string> >& banks = node_model()->get_programs(); + std::ostringstream oss; + + map<int, map<int, string> >::const_iterator i; + for (i = banks.begin(); i != banks.end(); ++i) { + Gtk::Menu* bank_menu; + if (banks.size() > 1) + bank_menu = manage(new Gtk::Menu()); + else + bank_menu = &_program_menu; + map<int, string>::const_iterator j; + for (j = i->second.begin(); j != i->second.end(); ++j) { + oss.str(""); + oss << std::setw(3) << std::setfill('0') + << j->first << ' ' << j->second; + sigc::slot<void> slt = bind( + bind(sigc::mem_fun(*this, &DSSIController::send_program_change), + j->first), i->first); + bank_menu->items().push_back( + Gtk::Menu_Helpers::MenuElem(oss.str(), slt)); + } + if (banks.size() > 1) { + oss.str(""); + oss << "Bank " << i->first; + _program_menu.items().push_back( + Gtk::Menu_Helpers::MenuElem(oss.str(), *bank_menu)); + } + } + + // Disable the program menu if there are no programs + if (banks.size() == 0) + _program_menu_item->set_sensitive(false); + else + _program_menu_item->set_sensitive(true); + + _banks_dirty = false; +#endif +} + + +void +DSSIController::send_program_change(int bank, int program) +{ + //App::instance().engine()->set_program(node_model()->path(), bank, program); +} + + +/** Attempt to show the DSSI GUI for this plugin. + * + * Returns whether or not GUI was successfully loaded/shown. + */ +bool +DSSIController::attempt_to_show_gui() +{ + cerr << "FIXME: DSSI GUI" << endl; +#if 0 + // Shamelessley "inspired by" jack-dssi-host + // Copyright 2004 Chris Cannam, Steve Harris and Sean Bolton. + + const bool verbose = false; + + string engine_url = App::instance().engine()->engine_url(); + // Hack off last character if it's a / + if (engine_url[engine_url.length()-1] == '/') + engine_url = engine_url.substr(0, engine_url.length()-1); + + const char* dllName = node_model()->plugin()->lib_name().c_str(); + const char* label = node_model()->plugin()->plug_label().c_str(); + const char* myName = "ingenuity"; + const string& oscUrl = engine_url + "/dssi" + node_model()->path(); + + struct dirent* entry = NULL; + char* dllBase = strdup(dllName); + char* subpath = NULL; + DIR* subdir = NULL; + char* filename = NULL; + struct stat buf; + int fuzzy = 0; + + char* env_dssi_path = getenv("DSSI_PATH"); + string dssi_path; + if (!env_dssi_path) { + cerr << "DSSI_PATH is empty. Assuming /usr/lib/dssi:/usr/local/lib/dssi." << endl; + dssi_path = "/usr/lib/dssi:/usr/local/lib/dssi"; + } else { + dssi_path = env_dssi_path; + } + + if (strlen(dllBase) > 3 && !strcasecmp(dllBase + strlen(dllBase) - 3, ".so")) { + dllBase[strlen(dllBase) - 3] = '\0'; + } + + + // This is pretty nasty, it loops through the path, even if the dllBase is absolute + while (dssi_path != "") { + string directory = dssi_path.substr(0, dssi_path.find(':')); + if (dssi_path.find(':') != string::npos) + dssi_path = dssi_path.substr(dssi_path.find(':')+1); + else + dssi_path = ""; + + if (*dllBase == '/') { + subpath = strdup(dllBase); + } else { + subpath = (char*)malloc(strlen(directory.c_str()) + strlen(dllBase) + 2); + sprintf(subpath, "%s/%s", directory.c_str(), dllBase); + } + + for (fuzzy = 0; fuzzy <= 1; ++fuzzy) { + + if (!(subdir = opendir(subpath))) { + if (verbose) { + fprintf(stderr, "%s: can't open plugin GUI directory \"%s\"\n", myName, subpath); + } + break; + } + + while ((entry = readdir(subdir))) { + + if (entry->d_name[0] == '.') + continue; + if (!strchr(entry->d_name, '_')) + continue; + + if (fuzzy) { + if (verbose) { + fprintf(stderr, "checking %s against %s\n", entry->d_name, dllBase); + } + if (strncmp(entry->d_name, dllBase, strlen(dllBase))) + continue; + } else { + if (verbose) { + fprintf(stderr, "checking %s against %s\n", entry->d_name, label); + } + if (strncmp(entry->d_name, label, strlen(label))) + continue; + } + + filename = (char*)malloc(strlen(subpath) + strlen(entry->d_name) + 2); + sprintf(filename, "%s/%s", subpath, entry->d_name); + + if (stat(filename, &buf)) { + perror("stat failed"); + free(filename); + continue; + } + + if ((S_ISREG(buf.st_mode) || S_ISLNK(buf.st_mode)) && + (buf.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) { + + if (verbose) { + fprintf(stderr, "%s: trying to execute GUI at \"%s\"\n", + myName, filename); + } + + if (fork() == 0) { + execlp(filename, filename, oscUrl.c_str(), dllName, label, + node_model()->name().c_str(), 0); + perror("exec failed"); + exit(1); + } + + free(filename); + free(subpath); + free(dllBase); + return true; + } + + free(filename); + } + } + } + + cerr << "Unable to launch DSSI GUI for " << node_model()->path() << endl; + + free(subpath); + free(dllBase); +#endif + return false; +} + + +void +DSSIController::show_menu(GdkEventButton* event) +{ +#if 0 + if (_banks_dirty) + update_program_menu(); + NodeController::show_menu(event); +#endif +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/libs/gui/DSSIController.h b/src/libs/gui/DSSIController.h new file mode 100644 index 00000000..fdb0c1c0 --- /dev/null +++ b/src/libs/gui/DSSIController.h @@ -0,0 +1,77 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DSSICONTROLLER_H +#define DSSICONTROLLER_H + +#include <string> +#include <gtkmm.h> +#include <raul/Path.h> +#include "client/NodeModel.h" + +using std::string; +using namespace Ingen::Client; + +namespace Ingen { namespace Client { + class MetadataModel; + class NodeModel; + class PortModel; +} } + +namespace Ingen { +namespace GUI { + +class NodeControlWindow; +class NodePropertiesWindow; + +/* Controller for a DSSI node. + * + * FIXME: legacy cruft. move this code to the appropriate places and nuke + * this class. + * + * \ingroup GUI + */ +class DSSIController +{ +public: + DSSIController(SharedPtr<NodeModel> model); + + virtual ~DSSIController() {} + + void show_gui(); + bool attempt_to_show_gui(); + void program_add(int bank, int program, const string& name); + void program_remove(int bank, int program); + + void send_program_change(int bank, int program); + + void show_menu(GdkEventButton* event); + +private: + void update_program_menu(); + + Gtk::Menu _program_menu; + Glib::RefPtr<Gtk::MenuItem> _program_menu_item; + + bool _banks_dirty; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // DSSICONTROLLER_H diff --git a/src/libs/gui/DSSIModule.cpp b/src/libs/gui/DSSIModule.cpp new file mode 100644 index 00000000..57cbd03f --- /dev/null +++ b/src/libs/gui/DSSIModule.cpp @@ -0,0 +1,43 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "DSSIModule.h" +#include "DSSIController.h" + +namespace Ingen { +namespace GUI { + + +DSSIModule::DSSIModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node) +: NodeModule(canvas, node) +{ +} + + +void +DSSIModule::on_double_click(GdkEventButton* ev) +{ + /* + DSSIController* dc = dynamic_cast<DSSIController*>(_node); + if (!dc || ! dc->attempt_to_show_gui()) + show_control_window(); + */ +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/DSSIModule.h b/src/libs/gui/DSSIModule.h new file mode 100644 index 00000000..1c041900 --- /dev/null +++ b/src/libs/gui/DSSIModule.h @@ -0,0 +1,46 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef DSSIMODULE_H +#define DSSIMODULE_H + +#include "NodeModule.h" + +namespace Ingen { +namespace GUI { + +class DSSIController; + +/* Module for a DSSI node. + * + * \ingroup GUI + */ +class DSSIModule : public NodeModule +{ +public: + DSSIModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node); + virtual ~DSSIModule() {} + + void on_double_click(GdkEventButton* ev); +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // DSSIMODULE_H + diff --git a/src/libs/gui/GladeFactory.cpp b/src/libs/gui/GladeFactory.cpp new file mode 100644 index 00000000..ac796d9f --- /dev/null +++ b/src/libs/gui/GladeFactory.cpp @@ -0,0 +1,72 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "GladeFactory.h" +#include <iostream> +#include <fstream> +using std::cout; using std::cerr; using std::endl; +using std::ifstream; + +namespace Ingen { +namespace GUI { + + +Glib::ustring GladeFactory::glade_filename = ""; + + +void +GladeFactory::find_glade_file() +{ + // Check for the .glade file in current directory + glade_filename = "./ingenuity.glade"; + ifstream fs(glade_filename.c_str()); + if (fs.fail()) { // didn't find it, check PKGDATADIR + fs.clear(); + glade_filename = PKGDATADIR; + glade_filename += "/ingenuity.glade"; + + fs.open(glade_filename.c_str()); + if (fs.fail()) { + cerr << "[GladeFactory] Unable to find ingenuity.glade in current directory or " << PKGDATADIR << "." << endl; + throw; + } + fs.close(); + } + cerr << "[GladeFactory] Loading widgets from " << glade_filename.c_str() << endl; +} + + +Glib::RefPtr<Gnome::Glade::Xml> +GladeFactory::new_glade_reference(const string& toplevel_widget) +{ + if (glade_filename == "") + find_glade_file(); + + try { + if (toplevel_widget == "") + return Gnome::Glade::Xml::create(glade_filename); + else + return Gnome::Glade::Xml::create(glade_filename, toplevel_widget.c_str()); + } catch (const Gnome::Glade::XmlError& ex) { + cerr << ex.what() << endl; + throw ex; + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/GladeFactory.h b/src/libs/gui/GladeFactory.h new file mode 100644 index 00000000..0a2e88fc --- /dev/null +++ b/src/libs/gui/GladeFactory.h @@ -0,0 +1,51 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GLADEFACTORY_H +#define GLADEFACTORY_H + +#include <string> +#include <libglademm/xml.h> + +using std::string; + +namespace Ingen { +namespace GUI { + + +/** Creates glade references, so various objects can create widgets. + * Purely static. + * + * \ingroup GUI + */ +class GladeFactory { +public: + static Glib::RefPtr<Gnome::Glade::Xml> + new_glade_reference(const string& toplevel_widget = ""); + +private: + GladeFactory() {} + + static void find_glade_file(); + static Glib::ustring glade_filename; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // GLADEFACTORY_H diff --git a/src/libs/gui/LashController.cpp b/src/libs/gui/LashController.cpp new file mode 100644 index 00000000..b62ec818 --- /dev/null +++ b/src/libs/gui/LashController.cpp @@ -0,0 +1,170 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "LashController.h" +#include "config.h" +#include <iostream> +#include <string> +#include <cassert> +#include <sys/stat.h> +#include <sys/types.h> +#include "App.h" +#include "PatchModel.h" + +using std::cerr; using std::cout; using std::endl; +using std::string; + +namespace Ingen { +namespace GUI { + + +LashController::LashController(lash_args_t* args) +: _client(NULL) +{ + _client = lash_init(args, PACKAGE_NAME, + /*LASH_Config_Data_Set|*/LASH_Config_File, LASH_PROTOCOL(2, 0)); + if (_client == NULL) { + cerr << "[LashController] Failed to connect to LASH. Session management will not function." << endl; + } else { + cout << "[LashController] Lash initialised" << endl; + } + + lash_event_t* event = lash_event_new_with_type(LASH_Client_Name); + lash_event_set_string(event, "Ingen"); + lash_send_event(_client, event); +} + + +LashController::~LashController() +{ + if (_client != NULL) { + lash_event_t* quit_event = lash_event_new_with_type(LASH_Quit); + lash_send_event(_client, quit_event); + } +} + + +void +LashController::process_events() +{ + assert(_client != NULL); + + lash_event_t* ev = NULL; + lash_config_t* conf = NULL; + + // Process events + while ((ev = lash_get_event(_client)) != NULL) { + handle_event(ev); + lash_event_destroy(ev); + } + + // Process configs + while ((conf = lash_get_config(_client)) != NULL) { + handle_config(conf); + lash_config_destroy(conf); + } +} + + +void +LashController::handle_event(lash_event_t* ev) +{ + assert(ev != NULL); + + LASH_Event_Type type = lash_event_get_type(ev); + const char* c_str = lash_event_get_string(ev); + string str = (c_str == NULL) ? "" : c_str; + + if (type == LASH_Save_File) { + cout << "[LashController] LASH Save File - " << str << endl; + save(str); + lash_send_event(_client, lash_event_new_with_type(LASH_Save_File)); + } else if (type == LASH_Restore_File) { + cout << "[LashController] LASH Restore File - " << str << endl; + cerr << "LASH RESTORE NOT YET (RE)IMPLEMENTED." << endl; + /*_controller->load_session_blocking(str + "/session"); + _controller->lash_restore_finished(); + lash_send_event(_client, lash_event_new_with_type(LASH_Restore_File)); + */ + /*} else if (type == LASH_Save_Data_Set) { + //cout << "[LashController] LASH Save Data Set - " << endl; + + // Tell LASH we're done + lash_send_event(_client, lash_event_new_with_type(LASH_Save_Data_Set)); + */ + } else if (type == LASH_Quit) { + cout << "[LashController] LASH Quit" << endl; + _client = NULL; + App::instance().quit(); + } else { + cerr << "[LashController] Unhandled LASH event, type: " << static_cast<int>(type) << endl; + } +} + + +void +LashController::handle_config(lash_config_t* conf) +{ + assert(conf != NULL); + + const char* key = NULL; + const void* val = NULL; + size_t val_size = 0; + + cout << "[LashController] LASH Config. Key = " << key << endl; + + key = lash_config_get_key(conf); + val = lash_config_get_value(conf); + val_size = lash_config_get_value_size(conf); +} + +void +LashController::save(const string& dir) +{ + cerr << "LASH SAVING NOT YET (RE)IMPLEMENTED\n"; + /* + PatchController* pc = NULL; + + // Save every patch under dir with it's path as a filename + // (so the dir structure will resemble the patch heirarchy) + for (map<string,PatchController*>::iterator i = _app->patches().begin(); + i != _app->patches().end(); ++i) { + pc = (*i).second; + pc->model()->filename(dir + pc->model()->path() + ".om"); + } + + // Create directories + for (map<string,PatchController*>::iterator i = _app->patches().begin(); + i != _app->patches().end(); ++i) { + pc = (*i).second; + if (pc->model()->parent() != NULL) { + string path = Path::parent(pc->model()->path()).substr(1) + "/"; + while (path.find("/") != string::npos) { + mkdir(string(dir +"/"+ path.substr(0, path.find("/"))).c_str(), 0744); + path = path.substr(path.find("/")+1); + } + } + _controller->save_patch_blocking(pc->model(), pc->model()->filename(), false); + } + + //m_app->state_manager()->save(str + "/omgtkrc"); + _controller->save_session_blocking(dir + "/session"); + */ +} + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/LashController.h b/src/libs/gui/LashController.h new file mode 100644 index 00000000..f47eb5e9 --- /dev/null +++ b/src/libs/gui/LashController.h @@ -0,0 +1,56 @@ +/* This file is part of IngenGtk. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LASHCONTROLLER_H +#define LASHCONTROLLER_H + +#include <string> +#include <lash/lash.h> +using std::string; + +namespace Ingen { +namespace GUI { + +class App; + +/* Old and unused LASH controller. + * + * \ingroup GUI + */ +class LashController +{ +public: + LashController(lash_args_t* args); + ~LashController(); + + bool enabled() { return lash_enabled(_client); } + void process_events(); + +private: + void save(const string& dir); + + lash_client_t* _client; + + void handle_event(lash_event_t* conf); + void handle_config(lash_config_t* conf); +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LASHCONTROLLER_H diff --git a/src/libs/gui/LoadPatchWindow.cpp b/src/libs/gui/LoadPatchWindow.cpp new file mode 100644 index 00000000..faaa519a --- /dev/null +++ b/src/libs/gui/LoadPatchWindow.cpp @@ -0,0 +1,149 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "LoadPatchWindow.h" +#include <sys/types.h> +#include <dirent.h> +#include <boost/optional/optional.hpp> +#include "interface/EngineInterface.h" +#include "client/PatchModel.h" +#include "App.h" +#include "Configuration.h" +#include "ThreadedLoader.h" + +using namespace Ingen::Serialisation; +using boost::optional; + +namespace Ingen { +namespace GUI { + + +LoadPatchWindow::LoadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: Gtk::FileChooserDialog(cobject), + _replace(true) +{ + xml->get_widget("load_patch_poly_from_current_radio", _poly_from_current_radio); + xml->get_widget("load_patch_poly_from_file_radio", _poly_from_file_radio); + xml->get_widget("load_patch_poly_from_user_radio", _poly_from_user_radio); + xml->get_widget("load_patch_poly_spinbutton", _poly_spinbutton); + xml->get_widget("load_patch_ok_button", _ok_button); + xml->get_widget("load_patch_cancel_button", _cancel_button); + + _poly_from_current_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_file_selected)); + _poly_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_file_selected)); + _poly_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadPatchWindow::poly_from_user_selected)); + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &LoadPatchWindow::ok_clicked)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadPatchWindow::cancel_clicked)); + + _poly_from_current_radio->set_active(true); + + Gtk::FileFilter filt; + filt.add_pattern("*.om"); + filt.set_name("Om patch files (XML, DEPRECATED) (*.om)"); + filt.add_pattern("*.ingen.ttl"); + filt.set_name("Ingen patch files (RDF, *.ingen.ttl)"); + set_filter(filt); + + // Add global examples directory to "shortcut folders" (bookmarks) + string examples_dir = PKGDATADIR; + examples_dir.append("/patches"); + DIR* d = opendir(examples_dir.c_str()); + if (d != NULL) + add_shortcut_folder(examples_dir); +} + + +void +LoadPatchWindow::present(SharedPtr<PatchModel> patch, MetadataMap data) +{ + set_patch(patch); + _initial_data = data; + Gtk::Window::present(); +} + + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +LoadPatchWindow::set_patch(SharedPtr<PatchModel> patch) +{ + _patch = patch; +} + + +void +LoadPatchWindow::on_show() +{ + if (App::instance().configuration()->patch_folder().length() > 0) + set_current_folder(App::instance().configuration()->patch_folder()); + Gtk::FileChooserDialog::on_show(); +} + + +///// Event Handlers ////// + + +void +LoadPatchWindow::poly_from_file_selected() +{ + _poly_spinbutton->property_sensitive() = false; +} + + +void +LoadPatchWindow::poly_from_user_selected() +{ + _poly_spinbutton->property_sensitive() = true; +} + + +void +LoadPatchWindow::ok_clicked() +{ + // If unset load_patch will load values + optional<const string&> name; + optional<size_t> poly; + + optional<Path> parent; + + if (_poly_from_user_radio->get_active()) + poly = _poly_spinbutton->get_value_as_int(); + + if (_replace) + App::instance().engine()->clear_patch(_patch->path()); + + if (_patch->path() != "/") + parent = _patch->path().parent(); + + App::instance().loader()->load_patch(true, get_uri(), "/", + _initial_data, parent, name, poly); + + hide(); +} + + +void +LoadPatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/LoadPatchWindow.h b/src/libs/gui/LoadPatchWindow.h new file mode 100644 index 00000000..47f0bc8f --- /dev/null +++ b/src/libs/gui/LoadPatchWindow.h @@ -0,0 +1,81 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LOADPATCHWINDOW_H +#define LOADPATCHWINDOW_H + +#include <libglademm/xml.h> +#include <gtkmm.h> +#include <raul/SharedPtr.h> +#include "client/PluginModel.h" +#include "client/PatchModel.h" +using Ingen::Client::PatchModel; +using Ingen::Client::MetadataMap; + +namespace Ingen { +namespace GUI { + + +/** 'Load Patch' window. + * + * Loaded by glade as a derived object. Used for both "Import" and "Load" + * (e.g. Replace, clear-then-import) operations (the radio button state + * should be changed with the provided methods before presenting). + * + * This is not for loading subpatches. See @a LoadSubpatchWindow for that. + * + * \ingroup GUI + */ +class LoadPatchWindow : public Gtk::FileChooserDialog +{ +public: + LoadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void set_patch(SharedPtr<PatchModel> patch); + + void set_replace() { _replace = true; } + void set_merge() { _replace = false; } + + void present(SharedPtr<PatchModel> patch, MetadataMap data); + +protected: + void on_show(); + +private: + void poly_from_file_selected(); + void poly_from_user_selected(); + void ok_clicked(); + void cancel_clicked(); + + MetadataMap _initial_data; + + SharedPtr<PatchModel> _patch; + bool _replace; + + Gtk::RadioButton* _poly_from_current_radio; + Gtk::RadioButton* _poly_from_file_radio; + Gtk::RadioButton* _poly_from_user_radio; + Gtk::SpinButton* _poly_spinbutton; + Gtk::Button* _ok_button; + Gtk::Button* _cancel_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADPATCHWINDOW_H diff --git a/src/libs/gui/LoadPluginWindow.cpp b/src/libs/gui/LoadPluginWindow.cpp new file mode 100644 index 00000000..a4b24e5f --- /dev/null +++ b/src/libs/gui/LoadPluginWindow.cpp @@ -0,0 +1,458 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <iostream> +#include <cassert> +#include <algorithm> +#include <cctype> +#include "interface/EngineInterface.h" +#include "client/NodeModel.h" +#include "client/PatchModel.h" +#include "client/Store.h" +#include "App.h" +#include "LoadPluginWindow.h" +#include "PatchWindow.h" +#include "PatchView.h" +#include "PatchCanvas.h" +using std::cout; using std::cerr; using std::endl; + + +namespace Ingen { +namespace GUI { + +LoadPluginWindow::LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: Gtk::Window(cobject), + _has_shown(false), + _plugin_name_offset(0) +{ + xml->get_widget("load_plugin_plugins_treeview", _plugins_treeview); + xml->get_widget("load_plugin_polyphonic_checkbutton", _polyphonic_checkbutton); + xml->get_widget("load_plugin_name_entry", _node_name_entry); + xml->get_widget("load_plugin_clear_button", _clear_button); + xml->get_widget("load_plugin_add_button", _add_button); + //xml->get_widget("load_plugin_close_button", _close_button); + //xml->get_widget("load_plugin_ok_button", _add_button); + + xml->get_widget("load_plugin_filter_combo", _filter_combo); + xml->get_widget("load_plugin_search_entry", _search_entry); + + // Set up the plugins list + _plugins_liststore = Gtk::ListStore::create(_plugins_columns); + _plugins_treeview->set_model(_plugins_liststore); + _plugins_treeview->append_column("Name", _plugins_columns._col_name); + _plugins_treeview->append_column("Type", _plugins_columns._col_type); + _plugins_treeview->append_column("URI", _plugins_columns._col_uri); + //m_plugins_treeview->append_column("Library", _plugins_columns._col_library); + //m_plugins_treeview->append_column("Label", _plugins_columns._col_label); + + // This could be nicer.. store the TreeViewColumns locally maybe? + _plugins_treeview->get_column(0)->set_sort_column(_plugins_columns._col_name); + _plugins_treeview->get_column(1)->set_sort_column(_plugins_columns._col_type); + _plugins_treeview->get_column(2)->set_sort_column(_plugins_columns._col_uri); + //m_plugins_treeview->get_column(3)->set_sort_column(_plugins_columns._col_library); + //m_plugins_treeview->get_column(4)->set_sort_column(_plugins_columns._col_label); + for (int i=0; i < 3; ++i) + _plugins_treeview->get_column(i)->set_resizable(true); + + _plugins_liststore->set_default_sort_func(sigc::mem_fun(this, &LoadPluginWindow::plugin_compare)); + + // Set up the search criteria combobox + _criteria_liststore = Gtk::ListStore::create(_criteria_columns); + _filter_combo->set_model(_criteria_liststore); + Gtk::TreeModel::iterator iter = _criteria_liststore->append(); + Gtk::TreeModel::Row row = *iter; + row[_criteria_columns._col_label] = "Name contains"; + row[_criteria_columns._col_criteria] = CriteriaColumns::NAME; + _filter_combo->set_active(iter); + iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "Type contains"; + row[_criteria_columns._col_criteria] = CriteriaColumns::TYPE; + iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "URI contains"; + row[_criteria_columns._col_criteria] = CriteriaColumns::URI; + /*iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "Library contains: "; + row[_criteria_columns._col_criteria] = CriteriaColumns::LIBRARY; + iter = _criteria_liststore->append(); row = *iter; + row[_criteria_columns._col_label] = "Label contains: "; + row[_criteria_columns._col_criteria] = CriteriaColumns::LABEL;*/ + + _clear_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::clear_clicked)); + _add_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked)); + //m_close_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::close_clicked)); + //m_add_button->signal_clicked().connect( sigc::mem_fun(this, &LoadPluginWindow::ok_clicked)); + _plugins_treeview->signal_row_activated().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_activated)); + _search_entry->signal_activate().connect( sigc::mem_fun(this, &LoadPluginWindow::add_clicked)); + _search_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::filter_changed)); + _node_name_entry->signal_changed().connect( sigc::mem_fun(this, &LoadPluginWindow::name_changed)); + + _selection = _plugins_treeview->get_selection(); + _selection->signal_changed().connect(sigc::mem_fun(this, &LoadPluginWindow::plugin_selection_changed)); + + //m_add_button->grab_default(); +} + + +void +LoadPluginWindow::present(SharedPtr<PatchModel> patch, MetadataMap data) +{ + set_patch(patch); + _initial_data = data; + Gtk::Window::present(); +} + + +/** Called every time the user types into the name input box. + * Used to display warning messages, and enable/disable the OK button. + */ +void +LoadPluginWindow::name_changed() +{ + string name = _node_name_entry->get_text(); + if (!Path::is_valid_name(name)) { + //m_message_label->set_text("Name contains invalid characters."); + _add_button->property_sensitive() = false; + } else if (_patch->get_node(name)) { + //m_message_label->set_text("An object already exists with that name."); + _add_button->property_sensitive() = false; + } else if (name.length() == 0) { + //m_message_label->set_text(""); + _add_button->property_sensitive() = false; + } else { + //m_message_label->set_text(""); + _add_button->property_sensitive() = true; + } +} + + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +LoadPluginWindow::set_patch(SharedPtr<PatchModel> patch) +{ + _patch = patch; + + if (patch->poly() <= 1) + _polyphonic_checkbutton->property_sensitive() = false; + else + _polyphonic_checkbutton->property_sensitive() = true; + +} + + +/** Populates the plugin list on the first show. + * + * This is done here instead of construction time as the list population is + * really expensive and bogs down creation of a patch. This is especially + * important when many patch notifications are sent at one time from the + * engine. + */ +void +LoadPluginWindow::on_show() +{ + if (!_has_shown) { + set_plugin_list(App::instance().store()->plugins()); + + // Center on patch window + /*int _w, _h; + get_size(_w, _h); + + int parent_x, parent_y, parent_w, parent_h; + _patch_controller->window()->get_position(parent_x, parent_y); + _patch_controller->window()->get_size(parent_w, parent_h); + + move(parent_x + parent_w/2 - _w/2, parent_y + parent_h/2 - _h/2); + */ + _has_shown = true; + } + Gtk::Window::on_show(); +} + + +int +LoadPluginWindow::plugin_compare(const Gtk::TreeModel::iterator& a_i, + const Gtk::TreeModel::iterator& b_i) +{ + SharedPtr<PluginModel> a = a_i->get_value(_plugins_columns._col_plugin_model); + SharedPtr<PluginModel> b = b_i->get_value(_plugins_columns._col_plugin_model); + + // FIXME: haaack + if (!a && !b) + return 0; + else if (!a) + return 1; + else if (!b) + return -1; + + if (a->type() == b->type()) + return strcmp(a->name().c_str(), b->name().c_str()); + else + return ((int)a->type() < (int)b->type()) ? -1 : 1; +} + + +void +LoadPluginWindow::set_plugin_list(const std::map<string, SharedPtr<PluginModel> >& m) +{ + _plugins_liststore->clear(); + + for (std::map<string, SharedPtr<PluginModel> >::const_iterator i = m.begin(); i != m.end(); ++i) { + SharedPtr<PluginModel> plugin = (*i).second; + + Gtk::TreeModel::iterator iter = _plugins_liststore->append(); + Gtk::TreeModel::Row row = *iter; + + row[_plugins_columns._col_name] = plugin->name(); + //row[_plugins_columns._col_label] = plugin->plug_label(); + if (plugin->type_uri() == "ingen:Internal") + row[_plugins_columns._col_type] = "Internal"; + else if (plugin->type_uri() == "ingen:LV2") + row[_plugins_columns._col_type] = "LV2"; + else if (plugin->type_uri() == "ingen:DSSI") + row[_plugins_columns._col_type] = "DSSI"; + else if (plugin->type_uri() == "ingen:LADSPA") + row[_plugins_columns._col_type] = "LADSPA"; + else + row[_plugins_columns._col_type] = plugin->type_uri(); + row[_plugins_columns._col_uri] = plugin->uri(); + row[_plugins_columns._col_label] = plugin->name(); + //row[_plugins_columns._col_library] = plugin->lib_name(); + row[_plugins_columns._col_plugin_model] = plugin; + } + + _plugins_liststore->set_sort_column(Gtk::TreeSortable::DEFAULT_SORT_COLUMN_ID, Gtk::SORT_ASCENDING); + + _plugins_treeview->columns_autosize(); +} + + +void +LoadPluginWindow::add_plugin(SharedPtr<PluginModel> plugin) +{ + Gtk::TreeModel::iterator iter = _plugins_liststore->append(); + Gtk::TreeModel::Row row = *iter; + + row[_plugins_columns._col_name] = plugin->name(); + //row[_plugins_columns._col_label] = plugin->plug_label(); + row[_plugins_columns._col_type] = plugin->type_uri(); + row[_plugins_columns._col_uri] = plugin->uri(); + row[_plugins_columns._col_label] = plugin->name(); + //row[_plugins_columns._col_library] = plugin->lib_name(); + row[_plugins_columns._col_plugin_model] = plugin; +} + + + +///// Event Handlers ////// + + +void +LoadPluginWindow::plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col) +{ + add_clicked(); +} + + +void +LoadPluginWindow::plugin_selection_changed() +{ + _plugin_name_offset = 0; + + _node_name_entry->set_text(generate_module_name()); + + //Gtk::TreeModel::iterator iter = _selection->get_selected(); + //Gtk::TreeModel::Row row = *iter; + //const PluginModel* plugin = row.get_value(_plugins_columns._col_plugin_model); +} + + +/** Generate an automatic name for this Node. + * + * Offset is an offset of the number that will be appended to the plugin's + * label, needed if the user adds multiple plugins faster than the engine + * sends the notification back. + */ +string +LoadPluginWindow::generate_module_name(int offset) +{ + string name = ""; + + Gtk::TreeModel::iterator iter = _selection->get_selected(); + + if (iter) { + Gtk::TreeModel::Row row = *iter; + SharedPtr<PluginModel> plugin = row.get_value(_plugins_columns._col_plugin_model); + return plugin->default_node_name(_patch); + } + /*char num_buf[3]; + for (uint i=0; i < 99; ++i) { + name = plugin->default_node_name(); + if (name == "") + name = plugin->name().substr(0, plugin->name().find(' ')); + if (i+offset != 0) { + snprintf(num_buf, 3, "%d", i+offset+1); + name += "_"; + name += num_buf; + } + if (!_patch->get_node(name)) + break; + else + name = ""; + } + }*/ + + return name; +} + + +void +LoadPluginWindow::add_clicked() +{ + Gtk::TreeModel::iterator iter = _selection->get_selected(); + bool polyphonic = _polyphonic_checkbutton->get_active(); + + if (iter) { // If anything is selected + Gtk::TreeModel::Row row = *iter; + SharedPtr<PluginModel> plugin = row.get_value(_plugins_columns._col_plugin_model); + string name = _node_name_entry->get_text(); + if (name == "") { + name = generate_module_name(); + } + if (name == "") { + Gtk::MessageDialog dialog(*this, + "Unable to chose a default name for this node. Please enter a name.", + false, Gtk::MESSAGE_ERROR, Gtk::BUTTONS_OK, true); + + dialog.run(); + } else { + Path path = _patch->path().base() + Path::nameify(name); + App::instance().engine()->create_node(path, plugin->uri(), polyphonic); + for (MetadataMap::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i) + App::instance().engine()->set_metadata(path, i->first, i->second); + ++_plugin_name_offset; + _node_name_entry->set_text(generate_module_name(_plugin_name_offset)); + + // Set the next module location 20 over, for a cascade effect + cerr << "FIXME: cascade\n"; + //m_new_module_x += 20; + //m_new_module_y += 20; + } + } +} + + +/* +void +LoadPluginWindow::close_clicked() +{ + hide(); +} + + +void +LoadPluginWindow::ok_clicked() +{ + add_clicked(); + close_clicked(); +} +*/ + +void +LoadPluginWindow::filter_changed() +{ + _plugins_liststore->clear(); + + string search = _search_entry->get_text(); + transform(search.begin(), search.end(), search.begin(), toupper); + + // Get selected criteria + const Gtk::TreeModel::Row row = *(_filter_combo->get_active()); + CriteriaColumns::Criteria criteria = row[_criteria_columns._col_criteria]; + + string field; + + Gtk::TreeModel::Row model_row; + Gtk::TreeModel::iterator model_iter; + size_t num_visible = 0; + + + for (std::map<string, SharedPtr<PluginModel> >::const_iterator i = App::instance().store()->plugins().begin(); + i != App::instance().store()->plugins().end(); ++i) { + + const SharedPtr<PluginModel> plugin = (*i).second; + + switch (criteria) { + case CriteriaColumns::NAME: + field = plugin->name(); break; + case CriteriaColumns::TYPE: + field = plugin->type_uri(); break; + case CriteriaColumns::URI: + field = plugin->uri(); break; + /*case CriteriaColumns::LIBRARY: + field = plugin->lib_name(); break; + case CriteriaColumns::LABEL: + field = plugin->plug_label(); break;*/ + default: + throw; + } + + transform(field.begin(), field.end(), field.begin(), toupper); + + if (field.find(search) != string::npos) { + model_iter = _plugins_liststore->append(); + model_row = *model_iter; + + model_row[_plugins_columns._col_name] = plugin->name(); + //model_row[_plugins_columns._col_label] = plugin->plug_label(); + model_row[_plugins_columns._col_type] = plugin->type_uri(); + model_row[_plugins_columns._col_uri] = plugin->uri(); + model_row[_plugins_columns._col_plugin_model] = plugin; + + ++num_visible; + } + } + + if (num_visible == 1) { + _selection->unselect_all(); + _selection->select(model_iter); + } +} + + +void +LoadPluginWindow::clear_clicked() +{ + _search_entry->set_text(""); + set_plugin_list(App::instance().store()->plugins()); +} + +bool +LoadPluginWindow::on_key_press_event(GdkEventKey* event) +{ + if (event->keyval == GDK_w && event->state & GDK_CONTROL_MASK) { + hide(); + return true; + } else { + return Gtk::Window::on_key_press_event(event); + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/LoadPluginWindow.h b/src/libs/gui/LoadPluginWindow.h new file mode 100644 index 00000000..1b39d314 --- /dev/null +++ b/src/libs/gui/LoadPluginWindow.h @@ -0,0 +1,151 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef LOADPLUGINWINDOW_H +#define LOADPLUGINWINDOW_H + +#include <map> +#include <libglademm/xml.h> +#include <libglademm.h> +#include <gtkmm.h> +#include <raul/SharedPtr.h> +#include "client/PatchModel.h" +#include "client/PluginModel.h" +using Ingen::Client::PluginModel; +using Ingen::Client::PatchModel; +using Ingen::Client::MetadataMap; + +namespace Ingen { +namespace GUI { + + +// Gtkmm _really_ needs to add some helper to abstract away this stupid nonsense + +/** Columns for the plugin list in the load plugin window. + * + * \ingroup GUI + */ +class ModelColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + ModelColumns() { + add(_col_name); + add(_col_type); + add(_col_uri); + add(_col_label); + //add(_col_library); + //add(_col_label); + add(_col_plugin_model); + } + + Gtk::TreeModelColumn<Glib::ustring> _col_name; + Gtk::TreeModelColumn<Glib::ustring> _col_type; + Gtk::TreeModelColumn<Glib::ustring> _col_uri; + + // Not displayed: + Gtk::TreeModelColumn<Glib::ustring> _col_label; + Gtk::TreeModelColumn<SharedPtr<PluginModel> > _col_plugin_model; +}; + + +/** Column for the criteria combo box in the load plugin window. + * + * \ingroup GUI + */ +class CriteriaColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + enum Criteria { NAME, TYPE, URI, }; + + CriteriaColumns() { add(_col_label); add(_col_criteria); } + + Gtk::TreeModelColumn<Glib::ustring> _col_label; + Gtk::TreeModelColumn<Criteria> _col_criteria; +}; + + +/** 'Load Plugin' window. + * + * Loaded by glade as a derived object. + * + * \ingroup GUI + */ +class LoadPluginWindow : public Gtk::Window +{ +public: + LoadPluginWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void set_patch(SharedPtr<PatchModel> patch); + void set_plugin_list(const std::map<string, SharedPtr<PluginModel> >& m); + + void add_plugin(SharedPtr<PluginModel> plugin); + bool has_shown() const { return _has_shown; } + + void present(SharedPtr<PatchModel> patch, MetadataMap data); + +protected: + void on_show(); + bool on_key_press_event(GdkEventKey* event); + +private: + void add_clicked(); + //void close_clicked(); + //void ok_clicked(); + void filter_changed(); + void clear_clicked(); + void name_changed(); + + int plugin_compare(const Gtk::TreeModel::iterator& a, + const Gtk::TreeModel::iterator& b); + + void plugin_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col); + void plugin_selection_changed(); + string generate_module_name(int offset = 0); + + MetadataMap _initial_data; + + SharedPtr<PatchModel> _patch; + + bool _has_shown; // plugin list only populated on show to speed patch window creation + + Glib::RefPtr<Gtk::ListStore> _plugins_liststore; + ModelColumns _plugins_columns; + + Glib::RefPtr<Gtk::ListStore> _criteria_liststore; + CriteriaColumns _criteria_columns; + + Glib::RefPtr<Gtk::TreeSelection> _selection; + + int _plugin_name_offset; // see comments for generate_plugin_name + + Gtk::TreeView* _plugins_treeview; + Gtk::CheckButton* _polyphonic_checkbutton; + Gtk::Entry* _node_name_entry; + Gtk::Button* _clear_button; + Gtk::Button* _add_button; + //Gtk::Button* _close_button; + //Gtk::Button* _ok_button; + Gtk::ComboBox* _filter_combo; + Gtk::Entry* _search_entry; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADPLUGINWINDOW_H diff --git a/src/libs/gui/LoadRemotePatchWindow.cpp b/src/libs/gui/LoadRemotePatchWindow.cpp new file mode 100644 index 00000000..534a6949 --- /dev/null +++ b/src/libs/gui/LoadRemotePatchWindow.cpp @@ -0,0 +1,163 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "LoadRemotePatchWindow.h" +#include <sys/types.h> +#include <dirent.h> +#include <boost/optional/optional.hpp> +#include <raul/RDFQuery.h> +#include "interface/EngineInterface.h" +#include "client/PatchModel.h" +#include "App.h" +#include "Configuration.h" +#include "ThreadedLoader.h" + +using boost::optional; +using namespace Raul; + +namespace Ingen { +namespace GUI { + + +LoadRemotePatchWindow::LoadRemotePatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: Gtk::Dialog(cobject), + _replace(true) +{ + xml->get_widget("load_remote_patch_treeview", _treeview); + xml->get_widget("load_remote_patch_uri_entry", _uri_entry); + xml->get_widget("load_remote_patch_cancel_button", _cancel_button); + xml->get_widget("load_remote_patch_open_button", _open_button); + + _liststore = Gtk::ListStore::create(_columns); + _treeview->set_model(_liststore); + _treeview->append_column("Name", _columns._col_name); + _treeview->append_column("URI", _columns._col_uri); + + _selection = _treeview->get_selection(); + _selection->signal_changed().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::patch_selected)); + _treeview->signal_row_activated().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::patch_activated)); + + _open_button->signal_clicked().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::open_clicked)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::cancel_clicked)); + _uri_entry->signal_changed().connect(sigc::mem_fun(this, &LoadRemotePatchWindow::uri_changed)); +} + + +void +LoadRemotePatchWindow::present(SharedPtr<PatchModel> patch, MetadataMap data) +{ + _liststore->clear(); + + set_patch(patch); + _initial_data = data; + + RDF::Model model(*App::instance().rdf_world(), + "http://rdf.drobilla.net/ingen_patches/index.ttl", + "http://rdf.drobilla.net/ingen_patches/"); + + RDF::Query query(*App::instance().rdf_world(), Glib::ustring( + "SELECT DISTINCT ?name ?uri WHERE {" + " ?uri a ingen:Patch ;" + " doap:name ?name ." + "}")); + + RDF::Query::Results results = query.run(*App::instance().rdf_world(), model); + + for (RDF::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + Gtk::TreeModel::iterator iter = _liststore->append(); + (*iter)[_columns._col_name] = (*i)["name"].to_string(); + (*iter)[_columns._col_uri] = (*i)["uri"].to_string(); + } + + _treeview->columns_autosize(); + + Gtk::Window::present(); +} + + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +LoadRemotePatchWindow::set_patch(SharedPtr<PatchModel> patch) +{ + _patch = patch; +} + + +void +LoadRemotePatchWindow::uri_changed() +{ + _open_button->property_sensitive() = (_uri_entry->get_text().length() > 0); +} + + +void +LoadRemotePatchWindow::patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col) +{ + open_clicked(); +} + + +void +LoadRemotePatchWindow::patch_selected() +{ + Gtk::TreeModel::iterator selected_i = _selection->get_selected(); + + if (selected_i) { // If anything is selected + const Glib::ustring uri = selected_i->get_value(_columns._col_uri); + _uri_entry->set_text(uri); + } +} + + +void +LoadRemotePatchWindow::open_clicked() +{ + Glib::ustring uri = _uri_entry->get_text(); + + cerr << "OPEN URI: " << uri << endl; + + // If unset load_patch will load values + optional<const string&> name; + optional<size_t> poly; + + optional<Path> parent; + + if (_replace) + App::instance().engine()->clear_patch(_patch->path()); + + if (_patch->path() != "/") + parent = _patch->path().parent(); + + App::instance().loader()->load_patch(true, uri, "/", + _initial_data, parent, name, poly); + + hide(); +} + + +void +LoadRemotePatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/LoadRemotePatchWindow.h b/src/libs/gui/LoadRemotePatchWindow.h new file mode 100644 index 00000000..bbb3d93e --- /dev/null +++ b/src/libs/gui/LoadRemotePatchWindow.h @@ -0,0 +1,93 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LOADREMOTEPATCHWINDOW_H +#define LOADREMOTEPATCHWINDOW_H + +#include <libglademm/xml.h> +#include <gtkmm.h> +#include <raul/SharedPtr.h> +#include "client/PatchModel.h" +#include "client/PluginModel.h" +using Ingen::Client::PatchModel; +using Ingen::Client::MetadataMap; + +namespace Ingen { +namespace GUI { + + +/** Columns for the remote patch list. + * + * \ingroup GUI + */ +class PatchColumns : public Gtk::TreeModel::ColumnRecord +{ +public: + PatchColumns() { + add(_col_name); + add(_col_uri); + } + + Gtk::TreeModelColumn<Glib::ustring> _col_name; + Gtk::TreeModelColumn<Glib::ustring> _col_uri; +}; + + + +/* Load remote patch ("import location") dialog. + * + * \ingroup GUI + */ +class LoadRemotePatchWindow : public Gtk::Dialog +{ +public: + LoadRemotePatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void set_patch(SharedPtr<PatchModel> patch); + + void set_replace() { _replace = true; } + void set_merge() { _replace = false; } + + void present(SharedPtr<PatchModel> patch, MetadataMap data); + +private: + void patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeViewColumn* col); + void patch_selected(); + void uri_changed(); + void open_clicked(); + void cancel_clicked(); + + MetadataMap _initial_data; + + SharedPtr<PatchModel> _patch; + bool _replace; + + Glib::RefPtr<Gtk::TreeSelection> _selection; + Glib::RefPtr<Gtk::ListStore> _liststore; + PatchColumns _columns; + + Gtk::TreeView* _treeview; + Gtk::Entry* _uri_entry; + Gtk::Button* _open_button; + Gtk::Button* _cancel_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADREMOTEPATCHWINDOW_H diff --git a/src/libs/gui/LoadSubpatchWindow.cpp b/src/libs/gui/LoadSubpatchWindow.cpp new file mode 100644 index 00000000..57fa9adc --- /dev/null +++ b/src/libs/gui/LoadSubpatchWindow.cpp @@ -0,0 +1,175 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <sys/types.h> +#include <dirent.h> +#include <cassert> +#include <boost/optional.hpp> +#include "interface/EngineInterface.h" +#include "client/NodeModel.h" +#include "client/PatchModel.h" +#include "App.h" +#include "LoadSubpatchWindow.h" +#include "PatchView.h" +#include "Configuration.h" +#include "ThreadedLoader.h" +using boost::optional; + +namespace Ingen { +namespace GUI { + + +LoadSubpatchWindow::LoadSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: Gtk::FileChooserDialog(cobject) +{ + xml->get_widget("load_subpatch_name_from_file_radio", _name_from_file_radio); + xml->get_widget("load_subpatch_name_from_user_radio", _name_from_user_radio); + xml->get_widget("load_subpatch_name_entry", _name_entry); + xml->get_widget("load_subpatch_poly_from_file_radio", _poly_from_file_radio); + xml->get_widget("load_subpatch_poly_from_parent_radio", _poly_from_parent_radio); + xml->get_widget("load_subpatch_poly_from_user_radio", _poly_from_user_radio); + xml->get_widget("load_subpatch_poly_spinbutton", _poly_spinbutton); + xml->get_widget("load_subpatch_ok_button", _ok_button); + xml->get_widget("load_subpatch_cancel_button", _cancel_button); + + _name_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_name_entry)); + _name_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::enable_name_entry)); + _poly_from_file_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_poly_spinner)); + _poly_from_parent_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::disable_poly_spinner)); + _poly_from_user_radio->signal_toggled().connect(sigc::mem_fun(this, &LoadSubpatchWindow::enable_poly_spinner)); + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &LoadSubpatchWindow::ok_clicked)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &LoadSubpatchWindow::cancel_clicked)); + + Gtk::FileFilter filt; + filt.add_pattern("*.om"); + filt.set_name("Om patch files (XML, DEPRECATED) (*.om)"); + filt.add_pattern("*.ingen.ttl"); + filt.set_name("Ingen patch files (RDF, *.ingen.ttl)"); + set_filter(filt); + + // Add global examples directory to "shortcut folders" (bookmarks) + string examples_dir = PKGDATADIR; + examples_dir.append("/patches"); + DIR* d = opendir(examples_dir.c_str()); + if (d != NULL) + add_shortcut_folder(examples_dir); +} + + +void +LoadSubpatchWindow::present(SharedPtr<PatchModel> patch, MetadataMap data) +{ + set_patch(patch); + _initial_data = data; + Gtk::Window::present(); +} + + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +LoadSubpatchWindow::set_patch(SharedPtr<PatchModel> patch) +{ + _patch = patch; + + char temp_buf[4]; + snprintf(temp_buf, 4, "%zd", patch->poly()); + Glib::ustring txt = "Same as parent ("; + txt.append(temp_buf).append(")"); + _poly_from_parent_radio->set_label(txt); +} + + +void +LoadSubpatchWindow::on_show() +{ + if (App::instance().configuration()->patch_folder().length() > 0) + set_current_folder(App::instance().configuration()->patch_folder()); + Gtk::FileChooserDialog::on_show(); +} + + +///// Event Handlers ////// + + + +void +LoadSubpatchWindow::disable_name_entry() +{ + _name_entry->property_sensitive() = false; +} + + +void +LoadSubpatchWindow::enable_name_entry() +{ + _name_entry->property_sensitive() = true; +} + + +void +LoadSubpatchWindow::disable_poly_spinner() +{ + _poly_spinbutton->property_sensitive() = false; +} + + +void +LoadSubpatchWindow::enable_poly_spinner() +{ + _poly_spinbutton->property_sensitive() = true; +} + + +void +LoadSubpatchWindow::ok_clicked() +{ + assert(_patch); + + // If unset load_patch will load values + optional<const string&> name; + optional<size_t> poly; + string name_str = ""; + + if (_name_from_user_radio->get_active()) { + name_str = _name_entry->get_text(); + name = name_str; + } + + if (_poly_from_user_radio->get_active()) + poly = _poly_spinbutton->get_value_as_int(); + else if (_poly_from_parent_radio->get_active()) + poly = _patch->poly(); + + App::instance().loader()->load_patch(false, get_uri(), "/", + _initial_data, _patch->path(), name, poly); + + hide(); +} + + +void +LoadSubpatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/LoadSubpatchWindow.h b/src/libs/gui/LoadSubpatchWindow.h new file mode 100644 index 00000000..08a6f7db --- /dev/null +++ b/src/libs/gui/LoadSubpatchWindow.h @@ -0,0 +1,79 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef LOADSUBPATCHWINDOW_H +#define LOADSUBPATCHWINDOW_H + +#include <libglademm/xml.h> +#include <gtkmm.h> +#include <raul/SharedPtr.h> +#include "client/PatchModel.h" +#include "client/PluginModel.h" +using Ingen::Client::PatchModel; +using Ingen::Client::MetadataMap; + +namespace Ingen { +namespace GUI { + + +/** 'Add Subpatch' window. + * + * Loaded by glade as a derived object. + * + * \ingroup GUI + */ +class LoadSubpatchWindow : public Gtk::FileChooserDialog +{ +public: + LoadSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void set_patch(SharedPtr<PatchModel> patch); + + void present(SharedPtr<PatchModel> patch, MetadataMap data); + +protected: + void on_show(); + +private: + void disable_name_entry(); + void enable_name_entry(); + void disable_poly_spinner(); + void enable_poly_spinner(); + + void ok_clicked(); + void cancel_clicked(); + + MetadataMap _initial_data; + + SharedPtr<PatchModel> _patch; + + Gtk::RadioButton* _name_from_file_radio; + Gtk::RadioButton* _name_from_user_radio; + Gtk::Entry* _name_entry; + Gtk::RadioButton* _poly_from_file_radio; + Gtk::RadioButton* _poly_from_parent_radio; + Gtk::RadioButton* _poly_from_user_radio; + Gtk::SpinButton* _poly_spinbutton; + Gtk::Button* _ok_button; + Gtk::Button* _cancel_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADSUBPATCHWINDOW_H diff --git a/src/libs/gui/Makefile.am b/src/libs/gui/Makefile.am new file mode 100644 index 00000000..c94ebbb5 --- /dev/null +++ b/src/libs/gui/Makefile.am @@ -0,0 +1,104 @@ +MAINTAINERCLEANFILES = Makefile.in +EXTRA_DIST = ingen_gui.gladep + +globalpixmapsdir = $(datadir)/pixmaps +dist_globalpixmaps_DATA = ingen.svg ingen-icon.svg + +sharefilesdir = $(pkgdatadir) +dist_sharefiles_DATA = ingen_gui.glade ingen.svg ingen-icon.svg + +moduledir = $(libdir)/ingen + +module_LTLIBRARIES = libingen_gui.la + +libingen_gui_la_CXXFLAGS = \ + -DPKGDATADIR=\"$(pkgdatadir)\" \ + -DINGEN_MODULE_DIR=\"$(libdir)/ingen\" \ + -DGTK_DISABLE_DEPRECATED -DGDK_DISABLE_DEPRECATED \ + @RAUL_CFLAGS@ @LOSC_CFLAGS@ @SLV2_CFLAGS@ @CURL_CFLAGS@ \ + @GTKMM_CFLAGS@ @LIBGLADEMM_CFLAGS@ @FLOWCANVAS_CFLAGS@ @GNOMECANVASMM_CFLAGS@ \ + -I$(top_srcdir)/src/common \ + -I$(top_srcdir)/src/libs + +libingen_gui_la_LDFLAGS = -no-undefined -module -avoid-version + +libingen_gui_la_LIBADD = \ + @RAUL_LIBS@ @LOSC_LIBS@ @SLV2_LIBS@ @CURL_LIBS@ \ + @GTKMM_LIBS@ @LIBGLADEMM_LIBS@ @FLOWCANVAS_LIBS@ @GNOMECANVASMM_LIBS@ \ + ../module/libingen_module.la \ + ../client/libingen_client.la + + +libingen_gui_la_SOURCES = \ + gui.h \ + gui.cpp \ + App.cpp \ + App.h \ + BreadCrumb.h \ + BreadCrumbBox.cpp \ + BreadCrumbBox.h \ + ConfigWindow.cpp \ + ConfigWindow.h \ + Configuration.cpp \ + Configuration.h \ + ConnectWindow.cpp \ + ConnectWindow.h \ + Connection.h \ + ControlGroups.cpp \ + ControlGroups.h \ + ControlPanel.cpp \ + ControlPanel.h \ + DSSIController.cpp \ + DSSIController.h \ + DSSIModule.cpp \ + DSSIModule.h \ + GladeFactory.cpp \ + GladeFactory.h \ + LoadPatchWindow.cpp \ + LoadPatchWindow.h \ + LoadPluginWindow.cpp \ + LoadPluginWindow.h \ + LoadRemotePatchWindow.cpp \ + LoadRemotePatchWindow.h \ + LoadSubpatchWindow.cpp \ + LoadSubpatchWindow.h \ + MessagesWindow.cpp \ + MessagesWindow.h \ + NewSubpatchWindow.cpp \ + NewSubpatchWindow.h \ + NodeControlWindow.cpp \ + NodeControlWindow.h \ + NodeMenu.cpp \ + NodeMenu.h \ + NodeModule.cpp \ + NodeModule.h \ + NodePropertiesWindow.cpp \ + NodePropertiesWindow.h \ + PatchCanvas.cpp \ + PatchCanvas.h \ + PatchPortModule.cpp \ + PatchPortModule.h \ + PatchPropertiesWindow.cpp \ + PatchPropertiesWindow.h \ + PatchTreeWindow.cpp \ + PatchTreeWindow.h \ + PatchView.cpp \ + PatchView.h \ + PatchWindow.cpp \ + PatchWindow.h \ + Port.cpp \ + Port.h \ + PortPropertiesWindow.cpp \ + PortPropertiesWindow.h \ + RenameWindow.cpp \ + RenameWindow.h \ + SubpatchModule.cpp \ + SubpatchModule.h \ + ThreadedLoader.cpp \ + ThreadedLoader.h \ + UploadPatchWindow.cpp \ + UploadPatchWindow.h \ + WindowFactory.cpp \ + WindowFactory.h + + diff --git a/src/libs/gui/MessagesWindow.cpp b/src/libs/gui/MessagesWindow.cpp new file mode 100644 index 00000000..b8a83c20 --- /dev/null +++ b/src/libs/gui/MessagesWindow.cpp @@ -0,0 +1,67 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "MessagesWindow.h" +#include <string> + +namespace Ingen { +namespace GUI { +using std::string; + + +MessagesWindow::MessagesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml) +: Gtk::Window(cobject) +{ + glade_xml->get_widget("messages_textview", _textview); + glade_xml->get_widget("messages_clear_button", _clear_button); + glade_xml->get_widget("messages_close_button", _close_button); + + _clear_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::clear_clicked)); + _close_button->signal_clicked().connect(sigc::mem_fun(this, &MessagesWindow::close_clicked)); +} + + +void +MessagesWindow::post(const string& msg) +{ + Glib::RefPtr<Gtk::TextBuffer> text_buf = _textview->get_buffer(); + text_buf->insert(text_buf->end(), msg); + text_buf->insert(text_buf->end(), "\n"); + + if (!_clear_button->is_sensitive()) + _clear_button->set_sensitive(true); +} + + +void +MessagesWindow::close_clicked() +{ + hide(); +} + + +void +MessagesWindow::clear_clicked() +{ + Glib::RefPtr<Gtk::TextBuffer> text_buf = _textview->get_buffer(); + text_buf->erase(text_buf->begin(), text_buf->end()); + _clear_button->set_sensitive(false); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/MessagesWindow.h b/src/libs/gui/MessagesWindow.h new file mode 100644 index 00000000..dea0fdd4 --- /dev/null +++ b/src/libs/gui/MessagesWindow.h @@ -0,0 +1,58 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef MESSAGESWINDOW_H +#define MESSAGESWINDOW_H + +#include <string> +#include <gtkmm.h> +#include <libglademm/xml.h> +using std::string; + + +namespace Ingen { +namespace GUI { + + +/** Messages Window. + * + * Loaded by libglade as a derived object. + * This is shown when errors occur (ie during patch loading). + * + * \ingroup GUI + */ +class MessagesWindow : public Gtk::Window +{ +public: + MessagesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); + + void post(const string& str); + +private: + void clear_clicked(); + void close_clicked(); + + Gtk::TextView* _textview; + Gtk::Button* _clear_button; + Gtk::Button* _close_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // MESSAGESWINDOW_H diff --git a/src/libs/gui/NewSubpatchWindow.cpp b/src/libs/gui/NewSubpatchWindow.cpp new file mode 100644 index 00000000..f0bc7caa --- /dev/null +++ b/src/libs/gui/NewSubpatchWindow.cpp @@ -0,0 +1,111 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "App.h" +#include "interface/EngineInterface.h" +#include "client/NodeModel.h" +#include "client/PatchModel.h" +#include "NewSubpatchWindow.h" +#include "PatchView.h" + +namespace Ingen { +namespace GUI { + + +NewSubpatchWindow::NewSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: Gtk::Window(cobject) +{ + xml->get_widget("new_subpatch_name_entry", _name_entry); + xml->get_widget("new_subpatch_message_label", _message_label); + xml->get_widget("new_subpatch_polyphony_spinbutton", _poly_spinbutton); + xml->get_widget("new_subpatch_ok_button", _ok_button); + xml->get_widget("new_subpatch_cancel_button", _cancel_button); + + _name_entry->signal_changed().connect(sigc::mem_fun(this, &NewSubpatchWindow::name_changed)); + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubpatchWindow::ok_clicked)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &NewSubpatchWindow::cancel_clicked)); + + _ok_button->property_sensitive() = false; +} + +void +NewSubpatchWindow::present(SharedPtr<PatchModel> patch, MetadataMap data) +{ + set_patch(patch); + _initial_data = data; + Gtk::Window::present(); +} + +/** Sets the patch controller for this window and initializes everything. + * + * This function MUST be called before using the window in any way! + */ +void +NewSubpatchWindow::set_patch(SharedPtr<PatchModel> patch) +{ + _patch = patch; +} + + +/** Called every time the user types into the name input box. + * Used to display warning messages, and enable/disable the OK button. + */ +void +NewSubpatchWindow::name_changed() +{ + string name = _name_entry->get_text(); + if (!Path::is_valid_name(name)) { + _message_label->set_text("Name contains invalid characters."); + _ok_button->property_sensitive() = false; + } else if (_patch->get_node(name)) { + _message_label->set_text("An object already exists with that name."); + _ok_button->property_sensitive() = false; + } else if (name.length() == 0) { + _message_label->set_text(""); + _ok_button->property_sensitive() = false; + } else { + _message_label->set_text(""); + _ok_button->property_sensitive() = true; + } +} + + +void +NewSubpatchWindow::ok_clicked() +{ + const Path path = _patch->path().base() + Path::nameify(_name_entry->get_text()); + const size_t poly = _poly_spinbutton->get_value_as_int(); + + App::instance().engine()->create_patch(path, poly); + for (MetadataMap::const_iterator i = _initial_data.begin(); i != _initial_data.end(); ++i) + App::instance().engine()->set_metadata(path, i->first, i->second); + + App::instance().engine()->enable_patch(path); + + hide(); +} + + +void +NewSubpatchWindow::cancel_clicked() +{ + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/NewSubpatchWindow.h b/src/libs/gui/NewSubpatchWindow.h new file mode 100644 index 00000000..e70b7f91 --- /dev/null +++ b/src/libs/gui/NewSubpatchWindow.h @@ -0,0 +1,67 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NEWSUBPATCHWINDOW_H +#define NEWSUBPATCHWINDOW_H + +#include <libglademm/xml.h> +#include <gtkmm.h> +#include <raul/SharedPtr.h> +#include "client/PatchModel.h" +#include "client/PluginModel.h" +using Ingen::Client::PatchModel; +using Ingen::Client::MetadataMap; + +namespace Ingen { +namespace GUI { + + +/** 'New Subpatch' window. + * + * Loaded by glade as a derived object. + * + * \ingroup GUI + */ +class NewSubpatchWindow : public Gtk::Window +{ +public: + NewSubpatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void set_patch(SharedPtr<PatchModel> patch); + + void present(SharedPtr<PatchModel> patch, MetadataMap data); + +private: + void name_changed(); + void ok_clicked(); + void cancel_clicked(); + + MetadataMap _initial_data; + SharedPtr<PatchModel> _patch; + + Gtk::Entry* _name_entry; + Gtk::Label* _message_label; + Gtk::SpinButton* _poly_spinbutton; + Gtk::Button* _ok_button; + Gtk::Button* _cancel_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // NEWSUBPATCHWINDOW_H diff --git a/src/libs/gui/NodeControlWindow.cpp b/src/libs/gui/NodeControlWindow.cpp new file mode 100644 index 00000000..26108833 --- /dev/null +++ b/src/libs/gui/NodeControlWindow.cpp @@ -0,0 +1,136 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License alongCont + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <iostream> +#include <cmath> +#include "interface/EngineInterface.h" +#include "client/NodeModel.h" +#include "App.h" +#include "NodeControlWindow.h" +#include "GladeFactory.h" +#include "ControlGroups.h" +#include "ControlPanel.h" +#include "PatchWindow.h" + +using namespace std; + +namespace Ingen { +namespace GUI { + + +/** Create a node control window and load a new ControlPanel for it. + */ +NodeControlWindow::NodeControlWindow(SharedPtr<NodeModel> node, size_t poly) +: _node(node), + _position_stored(false), + _x(0), _y(0) +{ + assert(_node != NULL); + + property_resizable() = true; + set_border_width(5); + + set_title(_node->path() + " Controls"); + + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("warehouse_win"); + xml->get_widget_derived("control_panel_vbox", _control_panel); + _control_panel->reparent(*this); + + _control_panel->init(_node, poly); + + show_all_children(); + resize(); + + _callback_enabled = true; +} + + +/** Create a node control window and with an existing ControlPanel. + */ +NodeControlWindow::NodeControlWindow(SharedPtr<NodeModel> node, ControlPanel* panel) +: _node(node), + _control_panel(panel) +{ + assert(_node); + + property_resizable() = true; + set_border_width(5); + + set_title(_node->path() + " Controls"); + + _control_panel->reparent(*this); + + show_all_children(); + resize(); + + _callback_enabled = true; +} + + +NodeControlWindow::~NodeControlWindow() +{ + delete _control_panel; +} + + +void +NodeControlWindow::resize() +{ + pair<int,int> controls_size = _control_panel->ideal_size(); + /*int width = 400; + int height = controls_size.second + + ((_node->polyphonic()) ? 4 : 40);*/ + int width = controls_size.first; + int height = controls_size.second; + + if (height > property_screen().get_value()->get_height() - 64) + height = property_screen().get_value()->get_height() - 64; + if (width > property_screen().get_value()->get_width() - 64) + width = property_screen().get_value()->get_width() - 64; + + //cerr << "Resizing to: " << width << " x " << height << endl; + + Gtk::Window::resize(width, height); +} + + +void +NodeControlWindow::on_show() +{ + for (PortModelList::const_iterator i = _node->ports().begin(); + i != _node->ports().end(); ++i) + if ((*i)->is_control() && (*i)->is_input()) + App::instance().engine()->request_port_value((*i)->path()); + + if (_position_stored) + move(_x, _y); + + Gtk::Window::on_show(); +} + + +void +NodeControlWindow::on_hide() +{ + _position_stored = true; + get_position(_x, _y); + Gtk::Window::on_hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/NodeControlWindow.h b/src/libs/gui/NodeControlWindow.h new file mode 100644 index 00000000..bd6f9fc9 --- /dev/null +++ b/src/libs/gui/NodeControlWindow.h @@ -0,0 +1,76 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODECONTROLWINDOW_H +#define NODECONTROLWINDOW_H + +#include <vector> +#include <string> +#include <gtkmm.h> +#include <libglademm.h> +#include <sigc++/sigc++.h> +#include <raul/SharedPtr.h> +using std::string; using std::vector; + +namespace Ingen { namespace Client { + class NodeModel; +} } +using Ingen::Client::NodeModel; + +namespace Ingen { +namespace GUI { + +class ControlGroup; +class ControlPanel; + + +/** Window with controls (sliders) for all control-rate ports on a Node. + * + * \ingroup GUI + */ +class NodeControlWindow : public Gtk::Window +{ +public: + NodeControlWindow(SharedPtr<NodeModel> node, size_t poly); + NodeControlWindow(SharedPtr<NodeModel> node, ControlPanel* panel); + virtual ~NodeControlWindow(); + + SharedPtr<NodeModel> node() { return _node; } + + ControlPanel* control_panel() const { return _control_panel; } + + void resize(); + +protected: + void on_show(); + void on_hide(); + +private: + SharedPtr<NodeModel> _node; + ControlPanel* _control_panel; + bool _callback_enabled; + + bool _position_stored; + int _x; + int _y; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // NODECONTROLWINDOW_H diff --git a/src/libs/gui/NodeMenu.cpp b/src/libs/gui/NodeMenu.cpp new file mode 100644 index 00000000..cf71989c --- /dev/null +++ b/src/libs/gui/NodeMenu.cpp @@ -0,0 +1,262 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <iostream> +#include <gtkmm.h> +#include "interface/EngineInterface.h" +#include "client/NodeModel.h" +#include "App.h" +#include "NodeMenu.h" +#include "WindowFactory.h" + +using std::cerr; using std::endl; + +namespace Ingen { +namespace GUI { + + +NodeMenu::NodeMenu(SharedPtr<NodeModel> node) +: _node(node) +, _controls_menuitem(NULL) +{ + App& app = App::instance(); + + Gtk::Menu_Helpers::MenuElem controls_elem = Gtk::Menu_Helpers::MenuElem("Controls", + sigc::bind( + sigc::mem_fun(app.window_factory(), &WindowFactory::present_controls), + node)); + _controls_menuitem = controls_elem.get_child(); + items().push_back(controls_elem); + + items().push_back(Gtk::Menu_Helpers::SeparatorElem()); + + /*items().push_back(Gtk::Menu_Helpers::MenuElem("Rename...", + sigc::bind( + sigc::mem_fun(app.window_factory(), &WindowFactory::present_rename), + node)));*/ + /*items().push_back(Gtk::Menu_Helpers::MenuElem("Clone", + sigc::bind( + sigc::mem_fun(app.engine(), &EngineInterface::clone), + node))); + sigc::mem_fun(this, &NodeMenu::on_menu_clone)));*/ + + items().push_back(Gtk::Menu_Helpers::MenuElem("Disconnect All", + sigc::mem_fun(this, &NodeMenu::on_menu_disconnect_all))); + + items().push_back(Gtk::Menu_Helpers::MenuElem("Destroy", + sigc::mem_fun(this, &NodeMenu::on_menu_destroy))); + + //m_controls_menuitem->property_sensitive() = false; + + cerr << "FIXME: MIDI learn menu\n"; + /* + if (_node->plugin() && _node->plugin()->type() == PluginModel::Internal + && _node->plugin()->plug_label() == "midi_control_in") { + items().push_back(Gtk::Menu_Helpers::MenuElem("Learn", + sigc::mem_fun(this, &NodeMenu::on_menu_learn))); + } + */ + + items().push_back(Gtk::Menu_Helpers::SeparatorElem()); + + items().push_back(Gtk::Menu_Helpers::MenuElem("Properties", + sigc::bind( + sigc::mem_fun(app.window_factory(), &WindowFactory::present_properties), + node))); + + //model->new_port_sig.connect(sigc::mem_fun(this, &NodeMenu::add_port)); + //model->destroyed_sig.connect(sigc::mem_fun(this, &NodeMenu::destroy)); +} + +#if 0 +NodeMenu::~NodeMenu() +{ + cerr << "~NodeMenu()\n"; +} + +void +NodeMenu::destroy() +{ + cerr << "FIXME: NODE DESTROYED\n"; + //SharedPtr<ObjectModel> model = _model; + //m_model.reset(); +} +#endif + +void +NodeMenu::set_path(const Path& new_path) +{ + cerr << "FIXME: rename\n"; + /* + remove_from_store(); + + // Rename ports + for (list<PortModel*>::const_iterator i = _node->ports().begin(); + i != _node->ports().end(); ++i) { + ObjectController* const pc = (*i)->controller(); + assert(pc != NULL); + pc->set_path(_model->path().base() + pc->model()->name()); + } + + // Handle bridge port, if this node represents one + if (_bridge_port != NULL) + _bridge_port->set_path(new_path); + + if (_module != NULL) + _module->canvas()->rename_module(_node->path().name(), new_path.name()); + + ObjectController::set_path(new_path); + + add_to_store(); + */ +} + +#if 0 +void +NodeMenu::destroy() +{ + PatchController* pc = ((PatchController*)_model->parent()->controller()); + assert(pc != NULL); + + //remove_from_store(); + //pc->remove_node(_model->path().name()); + cerr << "FIXME: remove node\n"; + + if (_bridge_port != NULL) + _bridge_port->destroy(); + _bridge_port = NULL; + + //if (_module != NULL) + // delete _module; +} +#endif + +#if 0 +void +NodeMenu::add_port(SharedPtr<PortModel> pm) +{ + assert(pm->parent().get() == _node.get()); + assert(pm->parent() == _node); + assert(_node->get_port(pm->path().name()) == pm); + + //cout << "[NodeMenu] Adding port " << pm->path() << endl; + + /* + if (_module != NULL) { + // (formerly PortController) + pc->create_port(_module); + _module->resize(); + + // Enable "Controls" menu item on module + if (has_control_inputs()) + enable_controls_menuitem(); + }*/ +} +#endif + +void +NodeMenu::on_menu_destroy() +{ + App::instance().engine()->destroy(_node->path()); +} + + +void +NodeMenu::on_menu_clone() +{ + cerr << "FIXME: clone broken\n"; + /* + assert(_node); + //assert(_parent != NULL); + //assert(_parent->model() != NULL); + + string clone_name = _node->name(); + int i = 2; // postfix number (ie oldname_2) + + // Check if name already has a number postfix + if (clone_name.find_last_of("_") != string::npos) { + string name_postfix = clone_name.substr(clone_name.find_last_of("_")+1); + clone_name = clone_name.substr(0, clone_name.find_last_of("_")); + if (sscanf(name_postfix.c_str(), "%d", &i)) + ++i; + } + + char clone_postfix[4]; + for ( ; i < 100; ++i) { + snprintf(clone_postfix, 4, "_%d", i); + if (_node->parent_patch()->get_node(clone_name + clone_postfix) == NULL) + break; + } + + clone_name = clone_name + clone_postfix; + + const string path = _node->parent_patch()->base() + clone_name; + NodeModel* nm = new NodeModel(_node->plugin(), path); + nm->polyphonic(_node->polyphonic()); + nm->x(_node->x() + 20); + nm->y(_node->y() + 20); + App::instance().engine()->create_node_from_model(nm); + */ +} + + +void +NodeMenu::on_menu_learn() +{ + App::instance().engine()->midi_learn(_node->path()); +} + +void +NodeMenu::on_menu_disconnect_all() +{ + App::instance().engine()->disconnect_all(_node->path()); +} + + +bool +NodeMenu::has_control_inputs() +{ + for (PortModelList::const_iterator i = _node->ports().begin(); + i != _node->ports().end(); ++i) + if ((*i)->is_input() && (*i)->is_control()) + return true; + + return false; +} + + +void +NodeMenu::enable_controls_menuitem() +{ + _controls_menuitem->property_sensitive() = true; +} + + +void +NodeMenu::disable_controls_menuitem() +{ + _controls_menuitem->property_sensitive() = false; + + //if (_control_window != NULL) + // _control_window->hide(); +} + + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/libs/gui/NodeMenu.h b/src/libs/gui/NodeMenu.h new file mode 100644 index 00000000..65044608 --- /dev/null +++ b/src/libs/gui/NodeMenu.h @@ -0,0 +1,78 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODEMENU_H +#define NODEMENU_H + +#include <string> +#include <gtkmm.h> +#include <raul/Path.h> +#include <raul/SharedPtr.h> +#include "client/NodeModel.h" +using Ingen::Client::NodeModel; + +using std::string; + +namespace Ingen { +namespace GUI { + +class Controller; +class NodeControlWindow; +class NodePropertiesWindow; +class PatchCanvas; + +/** Controller for a Node. + * + * \ingroup GUI + */ +class NodeMenu : public Gtk::Menu +{ +public: + NodeMenu(SharedPtr<NodeModel> node); + + void set_path(const Path& new_path); + + virtual void program_add(int bank, int program, const string& name) {} + virtual void program_remove(int bank, int program) {} + + bool has_control_inputs(); + + //virtual void show_menu(GdkEventButton* event) + //{ _menu.popup(event->button, event->time); } + +protected: + + virtual void enable_controls_menuitem(); + virtual void disable_controls_menuitem(); + + //virtual void add_port(SharedPtr<PortModel> pm); + + void on_menu_destroy(); + void on_menu_clone(); + void on_menu_learn(); + void on_menu_disconnect_all(); + + //Gtk::Menu _menu; + SharedPtr<NodeModel> _node; + Glib::RefPtr<Gtk::MenuItem> _controls_menuitem; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // NODEMENU_H diff --git a/src/libs/gui/NodeModule.cpp b/src/libs/gui/NodeModule.cpp new file mode 100644 index 00000000..3c80f5e2 --- /dev/null +++ b/src/libs/gui/NodeModule.cpp @@ -0,0 +1,153 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cassert> +#include <raul/Atom.h> +#include "interface/EngineInterface.h" +#include "client/PatchModel.h" +#include "client/NodeModel.h" +#include "App.h" +#include "NodeModule.h" +#include "PatchCanvas.h" +#include "Port.h" +#include "GladeFactory.h" +#include "RenameWindow.h" +#include "PatchWindow.h" +#include "WindowFactory.h" +#include "SubpatchModule.h" +#include "NodeControlWindow.h" + +namespace Ingen { +namespace GUI { + + +NodeModule::NodeModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node) +: LibFlowCanvas::Module(canvas, node->path().name()), + _node(node), + _menu(node) +{ + assert(_node); + + if (node->polyphonic()) { + set_border_width(2.0); + } + + node->new_port_sig.connect(sigc::bind(sigc::mem_fun(this, &NodeModule::add_port), true)); + node->removed_port_sig.connect(sigc::mem_fun(this, &NodeModule::remove_port)); + node->metadata_update_sig.connect(sigc::mem_fun(this, &NodeModule::metadata_update)); + + signal_clicked.connect(sigc::mem_fun(this, &NodeModule::on_click)); +} + + +NodeModule::~NodeModule() +{ + NodeControlWindow* win = App::instance().window_factory()->control_window(_node); + + if (win) { + // Should remove from window factory via signal + delete win; + } +} + + +boost::shared_ptr<NodeModule> +NodeModule::create(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node) +{ + boost::shared_ptr<NodeModule> ret; + + SharedPtr<PatchModel> patch = PtrCast<PatchModel>(node); + if (patch) + ret = boost::shared_ptr<NodeModule>(new SubpatchModule(canvas, patch)); + else + ret = boost::shared_ptr<NodeModule>(new NodeModule(canvas, node)); + + for (MetadataMap::const_iterator m = node->metadata().begin(); m != node->metadata().end(); ++m) + ret->metadata_update(m->first, m->second); + + for (PortModelList::const_iterator p = node->ports().begin(); p != node->ports().end(); ++p) + ret->add_port(*p, false); + + ret->resize(); + + return ret; +} + + +void +NodeModule::add_port(SharedPtr<PortModel> port, bool resize_to_fit) +{ + Module::add_port(boost::shared_ptr<Port>(new Port( + PtrCast<NodeModule>(shared_from_this()), port))); + + if (resize_to_fit) + resize(); +} + + +void +NodeModule::remove_port(SharedPtr<PortModel> port) +{ + SharedPtr<LibFlowCanvas::Port> p = Module::remove_port(port->path().name()); + p.reset(); +} + + +void +NodeModule::show_control_window() +{ + App::instance().window_factory()->present_controls(_node); +} + + +void +NodeModule::store_location() +{ + const float x = static_cast<float>(property_x()); + const float y = static_cast<float>(property_y()); + + const Atom& existing_x = _node->get_metadata("ingenuity:canvas-x"); + const Atom& existing_y = _node->get_metadata("ingenuity:canvas-y"); + + if (existing_x.type() != Atom::FLOAT || existing_y.type() != Atom::FLOAT + || existing_x.get_float() != x || existing_y.get_float() != y) { + App::instance().engine()->set_metadata(_node->path(), "ingenuity:canvas-x", Atom(x)); + App::instance().engine()->set_metadata(_node->path(), "ingenuity:canvas-y", Atom(y)); + } +} + + +void +NodeModule::on_click(GdkEventButton* event) +{ + if (event->button == 3) + _menu.popup(event->button, event->time); +} + + +void +NodeModule::metadata_update(const string& key, const Atom& value) +{ + if (key == "ingenuity:canvas-x" && value.type() == Atom::FLOAT) + move_to(value.get_float(), property_y()); + else if (key == "ingenuity:canvas-y" && value.type() == Atom::FLOAT) + move_to(property_x(), value.get_float()); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/NodeModule.h b/src/libs/gui/NodeModule.h new file mode 100644 index 00000000..8e88b34c --- /dev/null +++ b/src/libs/gui/NodeModule.h @@ -0,0 +1,90 @@ +/* This file is part of In* Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODEMODULE_H +#define NODEMODULE_H + +#include <string> +#include <libgnomecanvasmm.h> +#include <flowcanvas/Module.h> +#include <raul/SharedPtr.h> +#include "Port.h" +#include "NodeMenu.h" +using std::string; + +class Atom; + +namespace Ingen { namespace Client { + class PortModel; + class NodeModel; + class ControlModel; +} } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class PatchCanvas; +class Port; + + +/** A module in a patch. + * + * This base class is extended for various types of modules - SubpatchModule, + * DSSIModule, etc. + * + * \ingroup GUI + */ +class NodeModule : public LibFlowCanvas::Module +{ +public: + static boost::shared_ptr<NodeModule> create (boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node); + + virtual ~NodeModule(); + + boost::shared_ptr<Port> port(const string& port_name) { + return boost::dynamic_pointer_cast<Ingen::GUI::Port>( + Module::get_port(port_name)); + } + + virtual void store_location(); + + void on_click(GdkEventButton* event); + + void show_control_window(); + + SharedPtr<NodeModel> node() const { return _node; } + +protected: + NodeModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<NodeModel> node); + + virtual void on_double_click(GdkEventButton* ev) { show_control_window(); } + virtual void on_middle_click(GdkEventButton* ev) { show_control_window(); } + + void metadata_update(const string& key, const Atom& value); + + void add_port(SharedPtr<PortModel> port, bool resize=true); + void remove_port(SharedPtr<PortModel> port); + + SharedPtr<NodeModel> _node; + NodeMenu _menu; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // NODEMODULE_H diff --git a/src/libs/gui/NodePropertiesWindow.cpp b/src/libs/gui/NodePropertiesWindow.cpp new file mode 100644 index 00000000..0cd59e80 --- /dev/null +++ b/src/libs/gui/NodePropertiesWindow.cpp @@ -0,0 +1,67 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cassert> +#include <string> +#include "client/NodeModel.h" +#include "client/PluginModel.h" +#include "NodePropertiesWindow.h" + +namespace Ingen { +namespace GUI { +using std::string; + + +NodePropertiesWindow::NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml) +: Gtk::Window(cobject) +{ + glade_xml->get_widget("node_properties_path_label", _node_path_label); + glade_xml->get_widget("node_properties_polyphonic_checkbutton", _node_polyphonic_toggle); + glade_xml->get_widget("node_properties_plugin_type_label", _plugin_type_label); + glade_xml->get_widget("node_properties_plugin_uri_label", _plugin_uri_label); + glade_xml->get_widget("node_properties_plugin_name_label", _plugin_name_label); +} + + +/** Set the node this window is associated with. + * This function MUST be called before using this object in any way. + */ +void +NodePropertiesWindow::set_node(SharedPtr<NodeModel> node_model) +{ + assert(node_model); + + _node_model = node_model; + + set_title(node_model->path() + " Properties"); + + _node_path_label->set_text(node_model->path()); + _node_polyphonic_toggle->set_active(node_model->polyphonic()); + + SharedPtr<PluginModel> pm = node_model->plugin(); + + if (pm) { + _plugin_type_label->set_text(pm->type_uri()); + _plugin_uri_label->set_text(pm->uri()); + _plugin_name_label->set_text(pm->name()); + } +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/libs/gui/NodePropertiesWindow.h b/src/libs/gui/NodePropertiesWindow.h new file mode 100644 index 00000000..057de4ec --- /dev/null +++ b/src/libs/gui/NodePropertiesWindow.h @@ -0,0 +1,58 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef NODEPROPERTIESWINDOW_H +#define NODEPROPERTIESWINDOW_H + +#include <gtkmm.h> +#include <libglademm.h> +#include <raul/SharedPtr.h> +#include "client/NodeModel.h" +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + + +/** Node properties window. + * + * Loaded by libglade as a derived object. + * + * \ingroup GUI + */ +class NodePropertiesWindow : public Gtk::Window +{ +public: + NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); + + void present(SharedPtr<NodeModel> node_model) { set_node(node_model); Gtk::Window::present(); } + void set_node(SharedPtr<NodeModel> node_model); + +private: + + SharedPtr<NodeModel> _node_model; + Gtk::Label* _node_path_label; + Gtk::CheckButton* _node_polyphonic_toggle; + Gtk::Label* _plugin_type_label; + Gtk::Label* _plugin_uri_label; + Gtk::Label* _plugin_name_label; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // NODEPROPERTIESWINDOW_H diff --git a/src/libs/gui/PatchCanvas.cpp b/src/libs/gui/PatchCanvas.cpp new file mode 100644 index 00000000..623d0c7c --- /dev/null +++ b/src/libs/gui/PatchCanvas.cpp @@ -0,0 +1,525 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cassert> +#include <flowcanvas/FlowCanvas.h> +#include "interface/EngineInterface.h" +#include "client/PluginModel.h" +#include "client/PatchModel.h" +#include "client/NodeModel.h" +#include "client/Store.h" +#include "client/Serializer.h" +#include "App.h" +#include "PatchCanvas.h" +#include "PatchWindow.h" +#include "PatchPortModule.h" +#include "LoadPluginWindow.h" +#include "LoadSubpatchWindow.h" +#include "NewSubpatchWindow.h" +#include "Port.h" +#include "Connection.h" +#include "NodeModule.h" +#include "SubpatchModule.h" +#include "GladeFactory.h" +#include "WindowFactory.h" +#include "config.h" +using Ingen::Client::Store; +using Ingen::Client::Serializer; +using Ingen::Client::PluginModel; + +namespace Ingen { +namespace GUI { + + +PatchCanvas::PatchCanvas(SharedPtr<PatchModel> patch, int width, int height) +: FlowCanvas(width, height), + _patch(patch), + _last_click_x(0), + _last_click_y(0) +{ + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference(); + xml->get_widget("canvas_menu", _menu); + + xml->get_widget("canvas_menu_add_audio_input", _menu_add_audio_input); + xml->get_widget("canvas_menu_add_audio_output", _menu_add_audio_output); + xml->get_widget("canvas_menu_add_control_input", _menu_add_control_input); + xml->get_widget("canvas_menu_add_control_output", _menu_add_control_output); + xml->get_widget("canvas_menu_add_midi_input", _menu_add_midi_input); + xml->get_widget("canvas_menu_add_midi_output", _menu_add_midi_output); + xml->get_widget("canvas_menu_load_plugin", _menu_load_plugin); + xml->get_widget("canvas_menu_load_patch", _menu_load_patch); + xml->get_widget("canvas_menu_new_patch", _menu_new_patch); + + // Add port menu items + _menu_add_audio_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "audio_input", "ingen:audio", false)); + _menu_add_audio_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "audio_output", "ingen:audio", true)); + _menu_add_control_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "control_input", "ingen:control", false)); + _menu_add_control_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "control_output", "ingen:control", true)); + _menu_add_midi_input->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "midi_input", "ingen:midi", false)); + _menu_add_midi_output->signal_activate().connect( + sigc::bind(sigc::mem_fun(this, &PatchCanvas::menu_add_port), + "midi_output", "ingen:midi", true)); + + build_plugin_menu(); + + // Connect to model signals to track state + _patch->new_node_sig.connect(sigc::mem_fun(this, &PatchCanvas::add_node)); + _patch->removed_node_sig.connect(sigc::mem_fun(this, &PatchCanvas::remove_node)); + _patch->new_port_sig.connect(sigc::mem_fun(this, &PatchCanvas::add_port)); + _patch->removed_port_sig.connect(sigc::mem_fun(this, &PatchCanvas::remove_port)); + _patch->new_connection_sig.connect(sigc::mem_fun(this, &PatchCanvas::connection)); + _patch->removed_connection_sig.connect(sigc::mem_fun(this, &PatchCanvas::disconnection)); + + // Connect widget signals to do things + _menu_load_plugin->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_load_plugin)); + _menu_load_patch->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_load_patch)); + _menu_new_patch->signal_activate().connect(sigc::mem_fun(this, &PatchCanvas::menu_new_patch)); +} + + +void +PatchCanvas::build_plugin_class_menu(Gtk::Menu* menu, + SLV2PluginClass plugin_class, SLV2PluginClasses classes) +{ +#ifdef HAVE_SLV2 + // Add submenus + for (unsigned i=0; i < slv2_plugin_classes_size(classes); ++i) { + SLV2PluginClass c = slv2_plugin_classes_get_at(classes, i); + const char* parent = slv2_plugin_class_get_parent_uri(c); + + if (parent && !strcmp(parent, slv2_plugin_class_get_uri(plugin_class))) { + menu->items().push_back(Gtk::Menu_Helpers::MenuElem( + slv2_plugin_class_get_label(c))); + Gtk::MenuItem* menu_item = &(menu->items().back()); + Gtk::Menu* submenu = Gtk::manage(new Gtk::Menu()); + menu_item->set_submenu(*submenu); + build_plugin_class_menu(submenu, c, classes); + } + } + + + const Store::Plugins& plugins = App::instance().store()->plugins(); + + // Add plugins + for (Store::Plugins::const_iterator i = plugins.begin(); i != plugins.end(); ++i) { + SLV2Plugin p = i->second->slv2_plugin(); + if (p && slv2_plugin_get_class(p) == plugin_class) + menu->items().push_back(Gtk::Menu_Helpers::MenuElem(i->second->name(), + sigc::bind(sigc::mem_fun(this, &PatchCanvas::load_plugin), + i->second))); + } + + +#endif +} + + +void +PatchCanvas::build_plugin_menu() +{ +#ifdef HAVE_SLV2 + _menu->items().push_back(Gtk::Menu_Helpers::ImageMenuElem("Plugin", + *(manage(new Gtk::Image(Gtk::Stock::EXECUTE, Gtk::ICON_SIZE_MENU))))); + Gtk::MenuItem* plugin_menu_item = &(_menu->items().back()); + Gtk::Menu* plugin_menu = Gtk::manage(new Gtk::Menu()); + plugin_menu_item->set_submenu(*plugin_menu); + _menu->reorder_child(*plugin_menu_item, 2); + + SLV2PluginClass lv2_plugin = slv2_world_get_plugin_class(PluginModel::slv2_world()); + SLV2PluginClasses classes = slv2_world_get_plugin_classes(PluginModel::slv2_world()); + + build_plugin_class_menu(plugin_menu, lv2_plugin, classes); +#endif +} + + +void +PatchCanvas::build() +{ + boost::shared_ptr<PatchCanvas> shared_this = + boost::dynamic_pointer_cast<PatchCanvas>(shared_from_this()); + + // Create modules for nodes + for (NodeModelMap::const_iterator i = _patch->nodes().begin(); + i != _patch->nodes().end(); ++i) { + add_node((*i).second); + } + + // Create pseudo modules for ports (ports on this canvas, not on our module) + for (PortModelList::const_iterator i = _patch->ports().begin(); + i != _patch->ports().end(); ++i) { + add_port(*i); + } + + // Create connections + for (list<SharedPtr<ConnectionModel> >::const_iterator i = _patch->connections().begin(); + i != _patch->connections().end(); ++i) { + connection(*i); + } +} + + +void +PatchCanvas::arrange() +{ + LibFlowCanvas::FlowCanvas::arrange(); + + for (list<boost::shared_ptr<Item> >::iterator i = _items.begin(); i != _items.end(); ++i) + (*i)->store_location(); +} + + +void +PatchCanvas::add_node(SharedPtr<NodeModel> nm) +{ + boost::shared_ptr<PatchCanvas> shared_this = + boost::dynamic_pointer_cast<PatchCanvas>(shared_from_this()); + + SharedPtr<PatchModel> pm = PtrCast<PatchModel>(nm); + SharedPtr<NodeModule> module; + if (pm) + module = SubpatchModule::create(shared_this, pm); + else + module = NodeModule::create(shared_this, nm); + + add_item(module); + module->show(); + _views.insert(std::make_pair(nm, module)); +} + + +void +PatchCanvas::remove_node(SharedPtr<NodeModel> nm) +{ + Views::iterator i = _views.find(nm); + + if (i != _views.end()) { + remove_item(i->second); + _views.erase(i); + } +} + + +void +PatchCanvas::add_port(SharedPtr<PortModel> pm) +{ + boost::shared_ptr<PatchCanvas> shared_this = + boost::dynamic_pointer_cast<PatchCanvas>(shared_from_this()); + + SharedPtr<PatchPortModule> view = PatchPortModule::create(shared_this, pm); + _views.insert(std::make_pair(pm, view)); + add_item(view); + view->show(); +} + + +void +PatchCanvas::remove_port(SharedPtr<PortModel> pm) +{ + Views::iterator i = _views.find(pm); + + if (i != _views.end()) { + remove_item(i->second); + _views.erase(i); + } +} + + +SharedPtr<LibFlowCanvas::Port> +PatchCanvas::get_port_view(SharedPtr<PortModel> port) +{ + SharedPtr<LibFlowCanvas::Port> ret; + SharedPtr<LibFlowCanvas::Module> module = _views[port]; + + // Port on this patch + if (module) { + ret = (PtrCast<PatchPortModule>(module)) + ? *(PtrCast<PatchPortModule>(module)->ports().begin()) + : PtrCast<LibFlowCanvas::Port>(module); + } else { + module = PtrCast<NodeModule>(_views[port->parent()]); + if (module) + ret = module->get_port(port->path().name()); + } + + return ret; +} + + +void +PatchCanvas::connection(SharedPtr<ConnectionModel> cm) +{ + const SharedPtr<LibFlowCanvas::Port> src = get_port_view(cm->src_port()); + const SharedPtr<LibFlowCanvas::Port> dst = get_port_view(cm->dst_port()); + + if (src && dst) + add_connection(boost::shared_ptr<Connection>(new Connection(shared_from_this(), + cm, src, dst, src->color() + 0x22222200))); + else + cerr << "[PatchCanvas] ERROR: Unable to find ports to connect " + << cm->src_port_path() << " -> " << cm->dst_port_path() << endl; +} + + +void +PatchCanvas::disconnection(SharedPtr<ConnectionModel> cm) +{ + const SharedPtr<LibFlowCanvas::Port> src = get_port_view(cm->src_port()); + const SharedPtr<LibFlowCanvas::Port> dst = get_port_view(cm->dst_port()); + + if (src && dst) + remove_connection(src, dst); + else + cerr << "[PatchCanvas] ERROR: Unable to find ports to disconnect " + << cm->src_port_path() << " -> " << cm->dst_port_path() << endl; +} + + +void +PatchCanvas::connect(boost::shared_ptr<LibFlowCanvas::Connectable> src_port, + boost::shared_ptr<LibFlowCanvas::Connectable> dst_port) +{ + const boost::shared_ptr<Ingen::GUI::Port> src + = boost::dynamic_pointer_cast<Ingen::GUI::Port>(src_port); + + const boost::shared_ptr<Ingen::GUI::Port> dst + = boost::dynamic_pointer_cast<Ingen::GUI::Port>(dst_port); + + if (!src || !dst) + return; + + // Midi binding/learn shortcut + if (src->model()->is_midi() && dst->model()->is_control()) + { + cerr << "FIXME: MIDI binding" << endl; +#if 0 + SharedPtr<PluginModel> pm(new PluginModel(PluginModel::Internal, "", "midi_control_in", "")); + SharedPtr<NodeModel> nm(new NodeModel(pm, _patch->path().base() + + src->name() + "-" + dst->name(), false)); + nm->set_metadata("canvas-x", Atom((float) + (dst->module()->property_x() - dst->module()->width() - 20))); + nm->set_metadata("canvas-y", Atom((float) + (dst->module()->property_y()))); + App::instance().engine()->create_node_from_model(nm.get()); + App::instance().engine()->connect(src->model()->path(), nm->path() + "/MIDI_In"); + App::instance().engine()->connect(nm->path() + "/Out_(CR)", dst->model()->path()); + App::instance().engine()->midi_learn(nm->path()); + + // Set control node range to port's user range + + App::instance().engine()->set_port_value_queued(nm->path().base() + "Min", + dst->model()->get_metadata("user-min").get_float()); + App::instance().engine()->set_port_value_queued(nm->path().base() + "Max", + dst->model()->get_metadata("user-max").get_float()); +#endif + } else { + App::instance().engine()->connect(src->model()->path(), dst->model()->path()); + } +} + + +void +PatchCanvas::disconnect(boost::shared_ptr<LibFlowCanvas::Connectable> src_port, + boost::shared_ptr<LibFlowCanvas::Connectable> dst_port) +{ + const boost::shared_ptr<Ingen::GUI::Port> src + = boost::dynamic_pointer_cast<Ingen::GUI::Port>(src_port); + + const boost::shared_ptr<Ingen::GUI::Port> dst + = boost::dynamic_pointer_cast<Ingen::GUI::Port>(dst_port); + + App::instance().engine()->disconnect(src->model()->path(), + dst->model()->path()); +} + + +bool +PatchCanvas::canvas_event(GdkEvent* event) +{ + assert(event); + + switch (event->type) { + + case GDK_BUTTON_PRESS: + if (event->button.button == 3) { + _last_click_x = (int)event->button.x; + _last_click_y = (int)event->button.y; + show_menu(event); + } + break; + + /*case GDK_KEY_PRESS: + if (event->key.keyval == GDK_Delete) + destroy_selected(); + break; + */ + + default: + break; + } + + return FlowCanvas::canvas_event(event); +} + + +void +PatchCanvas::destroy_selection() +{ + for (list<boost::shared_ptr<Item> >::iterator m = _selected_items.begin(); m != _selected_items.end(); ++m) { + boost::shared_ptr<NodeModule> module = boost::dynamic_pointer_cast<NodeModule>(*m); + if (module) { + App::instance().engine()->destroy(module->node()->path()); + } else { + boost::shared_ptr<PatchPortModule> port_module = boost::dynamic_pointer_cast<PatchPortModule>(*m); + if (port_module) + App::instance().engine()->destroy(port_module->port()->path()); + } + } + +} + + +void +PatchCanvas::copy_selection() +{ + Serializer serializer(*App::instance().rdf_world()); + serializer.start_to_string(); + + for (list<boost::shared_ptr<Item> >::iterator m = _selected_items.begin(); m != _selected_items.end(); ++m) { + boost::shared_ptr<NodeModule> module = boost::dynamic_pointer_cast<NodeModule>(*m); + if (module) { + serializer.serialize(module->node()); + } else { + boost::shared_ptr<PatchPortModule> port_module = boost::dynamic_pointer_cast<PatchPortModule>(*m); + if (port_module) + serializer.serialize(port_module->port()); + } + } + + for (list<boost::shared_ptr<LibFlowCanvas::Connection> >::iterator c = _selected_connections.begin(); + c != _selected_connections.end(); ++c) { + boost::shared_ptr<Connection> connection = boost::dynamic_pointer_cast<Connection>(*c); + if (connection) + serializer.serialize_connection(connection->model()); + } + + string result = serializer.finish(); + + Glib::RefPtr<Gtk::Clipboard> clipboard = Gtk::Clipboard::get(); + clipboard->set_text(result); +} + + +string +PatchCanvas::generate_port_name(const string& base) { + string name = base; + + char num_buf[5]; + for (uint i=1; i < 9999; ++i) { + snprintf(num_buf, 5, "%u", i); + name = base + "_"; + name += num_buf; + if (!_patch->get_port(name)) + break; + } + + assert(Path::is_valid(string("/") + name)); + + return name; +} + + +void +PatchCanvas::menu_add_port(const string& name, const string& type, bool is_output) +{ + const Path& path = _patch->path().base() + generate_port_name(name); + App::instance().engine()->create_port(path, type, is_output); + MetadataMap data = get_initial_data(); + for (MetadataMap::const_iterator i = data.begin(); i != data.end(); ++i) + App::instance().engine()->set_metadata(path, i->first, i->second); +} + + +void +PatchCanvas::load_plugin(SharedPtr<PluginModel> plugin) +{ + const Path& path = _patch->path().base() + plugin->default_node_name(_patch); + // FIXME: polyphony? + App::instance().engine()->create_node(path, plugin->uri(), false); + MetadataMap data = get_initial_data(); + for (MetadataMap::const_iterator i = data.begin(); i != data.end(); ++i) + App::instance().engine()->set_metadata(path, i->first, i->second); +} + + +/** Try to guess a suitable location for a new module. + */ +void +PatchCanvas::get_new_module_location(double& x, double& y) +{ + int scroll_x; + int scroll_y; + get_scroll_offsets(scroll_x, scroll_y); + x = scroll_x + 20; + y = scroll_y + 20; +} + + +MetadataMap +PatchCanvas::get_initial_data() +{ + MetadataMap result; + + result["ingenuity:canvas-x"] = Atom((float)_last_click_x); + result["ingenuity:canvas-y"] = Atom((float)_last_click_y); + + return result; +} + +void +PatchCanvas::menu_load_plugin() +{ + App::instance().window_factory()->present_load_plugin(_patch, get_initial_data()); +} + + +void +PatchCanvas::menu_load_patch() +{ + App::instance().window_factory()->present_load_subpatch(_patch, get_initial_data()); +} + + +void +PatchCanvas::menu_new_patch() +{ + App::instance().window_factory()->present_new_subpatch(_patch, get_initial_data()); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/PatchCanvas.h b/src/libs/gui/PatchCanvas.h new file mode 100644 index 00000000..8634b7bf --- /dev/null +++ b/src/libs/gui/PatchCanvas.h @@ -0,0 +1,129 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHCANVAS_H +#define PATCHCANVAS_H + +#include <string> +#include <map> +#include <boost/shared_ptr.hpp> +#include <flowcanvas/FlowCanvas.h> +#include <flowcanvas/Module.h> +#include <raul/SharedPtr.h> +#include <raul/Path.h> +#include "client/ConnectionModel.h" +#include "client/PatchModel.h" +#include "NodeModule.h" + +using std::string; +using namespace LibFlowCanvas; + +using LibFlowCanvas::Port; +using Ingen::Client::ConnectionModel; +using Ingen::Client::PatchModel; +using Ingen::Client::NodeModel; +using Ingen::Client::PortModel; +using Ingen::Client::MetadataMap; + +namespace Ingen { +namespace GUI { + +class NodeModule; + + +/** Patch canvas widget. + * + * \ingroup GUI + */ +class PatchCanvas : public LibFlowCanvas::FlowCanvas +{ +public: + PatchCanvas(SharedPtr<PatchModel> patch, int width, int height); + + virtual ~PatchCanvas() {} + + /*boost::shared_ptr<NodeModule> find_module(const string& name) { + return boost::dynamic_pointer_cast<NodeModule>( + FlowCanvas::get_item(name)); + }*/ + + void build(); + void arrange(); + + void add_node(SharedPtr<NodeModel> nm); + void remove_node(SharedPtr<NodeModel> nm); + void add_port(SharedPtr<PortModel> pm); + void remove_port(SharedPtr<PortModel> pm); + void connection(SharedPtr<ConnectionModel> cm); + void disconnection(SharedPtr<ConnectionModel> cm); + + void get_new_module_location(double& x, double& y); + + void destroy_selection(); + void copy_selection(); + + void show_menu(GdkEvent* event) + { _menu->popup(event->button.button, event->button.time); } + +private: + string generate_port_name(const string& base); + void menu_add_port(const string& name, const string& type, bool is_output); + void menu_load_plugin(); + void menu_new_patch(); + void menu_load_patch(); + void load_plugin(SharedPtr<PluginModel> plugin); + void build_plugin_menu(); + void build_plugin_class_menu(Gtk::Menu* menu, + SLV2PluginClass plugin_class, SLV2PluginClasses classes); + + MetadataMap get_initial_data(); + + bool canvas_event(GdkEvent* event); + + SharedPtr<LibFlowCanvas::Port> get_port_view(SharedPtr<PortModel> port); + + void connect(boost::shared_ptr<LibFlowCanvas::Connectable> src, + boost::shared_ptr<LibFlowCanvas::Connectable> dst); + + void disconnect(boost::shared_ptr<LibFlowCanvas::Connectable> src, + boost::shared_ptr<LibFlowCanvas::Connectable> dst); + + SharedPtr<PatchModel> _patch; + + typedef std::map<SharedPtr<ObjectModel>, SharedPtr<LibFlowCanvas::Module> > Views; + Views _views; + + int _last_click_x; + int _last_click_y; + + Gtk::Menu* _menu; + Gtk::MenuItem* _menu_add_audio_input; + Gtk::MenuItem* _menu_add_audio_output; + Gtk::MenuItem* _menu_add_control_input; + Gtk::MenuItem* _menu_add_control_output; + Gtk::MenuItem* _menu_add_midi_input; + Gtk::MenuItem* _menu_add_midi_output; + Gtk::MenuItem* _menu_load_plugin; + Gtk::MenuItem* _menu_load_patch; + Gtk::MenuItem* _menu_new_patch; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHCANVAS_H diff --git a/src/libs/gui/PatchPortModule.cpp b/src/libs/gui/PatchPortModule.cpp new file mode 100644 index 00000000..2a812296 --- /dev/null +++ b/src/libs/gui/PatchPortModule.cpp @@ -0,0 +1,113 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "PatchPortModule.h" +#include <cassert> +#include "interface/EngineInterface.h" +#include "client/PatchModel.h" +#include "client/NodeModel.h" +#include "App.h" +#include "PatchCanvas.h" +#include "Port.h" +#include "GladeFactory.h" +#include "RenameWindow.h" +#include "PatchWindow.h" + +namespace Ingen { +namespace GUI { + + +PatchPortModule::PatchPortModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PortModel> port) +: LibFlowCanvas::Module(canvas, port->path().name(), 0, 0, false), // FIXME: coords? + _port(port) +{ + /*if (port_model()->polyphonic() && port_model()->parent() != NULL + && port_model()->parent_patch()->poly() > 1) { + border_width(2.0); + }*/ + + assert(canvas); + assert(port); + + assert(PtrCast<PatchModel>(port->parent())); + + /*resize(); + + const Atom& x_atom = port->get_metadata("ingenuity:canvas-x"); + const Atom& y_atom = port->get_metadata("ingenuity:canvas-y"); + + if (x_atom && y_atom && x_atom.type() == Atom::FLOAT && y_atom.type() == Atom::FLOAT) { + move_to(x_atom.get_float(), y_atom.get_float()); + } else { + double default_x; + double default_y; + canvas->get_new_module_location(default_x, default_y); + move_to(default_x, default_y); + }*/ + + port->metadata_update_sig.connect(sigc::mem_fun(this, &PatchPortModule::metadata_update)); +} + + +boost::shared_ptr<PatchPortModule> +PatchPortModule::create(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PortModel> port) +{ + boost::shared_ptr<PatchPortModule> ret = boost::shared_ptr<PatchPortModule>( + new PatchPortModule(canvas, port)); + assert(ret); + + ret->_patch_port = boost::shared_ptr<Port>(new Port(ret, port, true, true)); + ret->add_port(ret->_patch_port); + + for (MetadataMap::const_iterator m = port->metadata().begin(); m != port->metadata().end(); ++m) + ret->metadata_update(m->first, m->second); + + ret->resize(); + + return ret; +} + + +void +PatchPortModule::store_location() +{ + const float x = static_cast<float>(property_x()); + const float y = static_cast<float>(property_y()); + + const Atom& existing_x = _port->get_metadata("ingenuity:canvas-x"); + const Atom& existing_y = _port->get_metadata("ingenuity:canvas-y"); + + if (existing_x.type() != Atom::FLOAT || existing_y.type() != Atom::FLOAT + || existing_x.get_float() != x || existing_y.get_float() != y) { + App::instance().engine()->set_metadata(_port->path(), "ingenuity:canvas-x", Atom(x)); + App::instance().engine()->set_metadata(_port->path(), "ingenuity:canvas-y", Atom(y)); + } +} + + +void +PatchPortModule::metadata_update(const string& key, const Atom& value) +{ + if (key == "ingenuity:canvas-x" && value.type() == Atom::FLOAT) + move_to(value.get_float(), property_y()); + else if (key == "ingenuity:canvas-y" && value.type() == Atom::FLOAT) + move_to(property_x(), value.get_float()); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/PatchPortModule.h b/src/libs/gui/PatchPortModule.h new file mode 100644 index 00000000..ac39eadf --- /dev/null +++ b/src/libs/gui/PatchPortModule.h @@ -0,0 +1,80 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHPORTMODULE_H +#define PATCHPORTMODULE_H + +#include <string> +#include <boost/enable_shared_from_this.hpp> +#include <libgnomecanvasmm.h> +#include <flowcanvas/Module.h> +#include <raul/Atom.h> +#include "Port.h" +using std::string; + +namespace Ingen { namespace Client { + class PortModel; + class NodeModel; + class ControlModel; +} } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class PatchCanvas; +class Port; + + +/** A "module" to represent a patch's port on it's own canvas. + * + * Translation: This is the nameless single port pseudo module thingy. + * + * \ingroup GUI + */ +class PatchPortModule : public boost::enable_shared_from_this<LibFlowCanvas::Module>, + public LibFlowCanvas::Module +{ +public: + static boost::shared_ptr<PatchPortModule> create(boost::shared_ptr<PatchCanvas> canvas, + SharedPtr<PortModel> port); + + virtual ~PatchPortModule() {} + + virtual void store_location(); + + //void on_right_click(GdkEventButton* event) { _port->show_menu(event); } + + SharedPtr<PortModel> port() const { return _port; } + +protected: + PatchPortModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PortModel> port); + + //virtual void on_double_click(GdkEventButton* ev) { show_control_window(); } + //virtual void on_middle_click(GdkEventButton* ev) { show_control_window(); } + + void metadata_update(const string& key, const Raul::Atom& value); + + SharedPtr<PortModel> _port; + boost::shared_ptr<Port> _patch_port; ///< Port on this 'anonymous' module +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHPORTMODULE_H diff --git a/src/libs/gui/PatchPropertiesWindow.cpp b/src/libs/gui/PatchPropertiesWindow.cpp new file mode 100644 index 00000000..1e310f0b --- /dev/null +++ b/src/libs/gui/PatchPropertiesWindow.cpp @@ -0,0 +1,89 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <string> +#include "client/PatchModel.h" +#include "PatchPropertiesWindow.h" + +namespace Ingen { +namespace GUI { +using std::string; + + +PatchPropertiesWindow::PatchPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml) +: Gtk::Window(cobject) +{ + glade_xml->get_widget("properties_author_entry", _author_entry); + glade_xml->get_widget("properties_description_textview", _textview); + glade_xml->get_widget("properties_cancel_button", _cancel_button); + glade_xml->get_widget("properties_ok_button", _ok_button); + + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &PatchPropertiesWindow::cancel_clicked)); + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &PatchPropertiesWindow::ok_clicked)); +} + + +/** Set the patch model this description is for. + * + * This function is a "post-constructor" - it MUST be called before using + * the window in any way. + */ +void +PatchPropertiesWindow::set_patch(SharedPtr<PatchModel> patch_model) +{ + property_title() = patch_model->path() + " Properties"; + _patch_model = patch_model; + + const Atom& author_atom = _patch_model->get_metadata("author"); + _author_entry->set_text( + (author_atom.type() == Atom::STRING) ? author_atom.get_string() : "" ); + + const Atom& desc_atom = _patch_model->get_metadata("description"); + _textview->get_buffer()->set_text( + (desc_atom.type() == Atom::STRING) ? desc_atom.get_string() : "" ); +} + + +void +PatchPropertiesWindow::cancel_clicked() +{ + const Atom& author_atom = _patch_model->get_metadata("author"); + _author_entry->set_text( + (author_atom.type() == Atom::STRING) ? author_atom.get_string() : "" ); + + const Atom& desc_atom = _patch_model->get_metadata("description"); + _textview->get_buffer()->set_text( + (desc_atom.type() == Atom::STRING) ? desc_atom.get_string() : "" ); + + hide(); +} + + +void +PatchPropertiesWindow::ok_clicked() +{ + cerr << "FIXME: properties\n"; + + //m_patch_model->set_metadata("author", Atom(_author_entry->get_text().c_str())); + //m_patch_model->set_metadata("description", Atom(_textview->get_buffer()->get_text().c_str())); + hide(); +} + + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/PatchPropertiesWindow.h b/src/libs/gui/PatchPropertiesWindow.h new file mode 100644 index 00000000..0640a198 --- /dev/null +++ b/src/libs/gui/PatchPropertiesWindow.h @@ -0,0 +1,64 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHPROPERTIESWINDOW_H +#define PATCHPROPERTIESWINDOW_H + +#include <string> +#include <gtkmm.h> +#include <libglademm/xml.h> +#include <raul/SharedPtr.h> +using std::string; + +namespace Ingen { namespace Client { class PatchModel; } } +using Ingen::Client::PatchModel; + +namespace Ingen { +namespace GUI { + + +/** Patch Properties Window. + * + * Loaded by libglade as a derived object. + * + * \ingroup GUI + */ +class PatchPropertiesWindow : public Gtk::Window +{ +public: + PatchPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); + + void present(SharedPtr<PatchModel> patch_model) { set_patch(patch_model); Gtk::Window::present(); } + void set_patch(SharedPtr<PatchModel> patch_model); + + void cancel_clicked(); + void ok_clicked(); + +private: + SharedPtr<PatchModel> _patch_model; + + Gtk::Entry* _author_entry; + Gtk::TextView* _textview; + Gtk::Button* _cancel_button; + Gtk::Button* _ok_button; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHPROPERTIESWINDOW_H diff --git a/src/libs/gui/PatchTreeWindow.cpp b/src/libs/gui/PatchTreeWindow.cpp new file mode 100644 index 00000000..b77508c1 --- /dev/null +++ b/src/libs/gui/PatchTreeWindow.cpp @@ -0,0 +1,270 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <raul/Path.h> +#include "interface/EngineInterface.h" +#include "client/OSCEngineSender.h" +#include "client/Store.h" +#include "client/PatchModel.h" +#include "App.h" +#include "PatchTreeWindow.h" +#include "SubpatchModule.h" +#include "WindowFactory.h" + +namespace Ingen { +namespace GUI { + + +PatchTreeWindow::PatchTreeWindow(BaseObjectType* cobject, + const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: Gtk::Window(cobject), + _enable_signal(true) +{ + xml->get_widget_derived("patches_treeview", _patches_treeview); + + _patch_treestore = Gtk::TreeStore::create(_patch_tree_columns); + _patches_treeview->set_window(this); + _patches_treeview->set_model(_patch_treestore); + Gtk::TreeViewColumn* name_col = Gtk::manage(new Gtk::TreeViewColumn( + "Patch", _patch_tree_columns.name_col)); + Gtk::TreeViewColumn* enabled_col = Gtk::manage(new Gtk::TreeViewColumn( + "Run", _patch_tree_columns.enabled_col)); + name_col->set_resizable(true); + name_col->set_expand(true); + + _patches_treeview->append_column(*name_col); + _patches_treeview->append_column(*enabled_col); + Gtk::CellRendererToggle* enabled_renderer = dynamic_cast<Gtk::CellRendererToggle*>( + _patches_treeview->get_column_cell_renderer(1)); + enabled_renderer->property_activatable() = true; + + _patch_tree_selection = _patches_treeview->get_selection(); + + //m_patch_tree_selection->signal_changed().connect( + // sigc::mem_fun(this, &PatchTreeWindow::event_patch_selected)); + _patches_treeview->signal_row_activated().connect( + sigc::mem_fun(this, &PatchTreeWindow::event_patch_activated)); + enabled_renderer->signal_toggled().connect( + sigc::mem_fun(this, &PatchTreeWindow::event_patch_enabled_toggled)); + + _patches_treeview->columns_autosize(); +} + + +void +PatchTreeWindow::init(Store& store) +{ + store.new_object_sig.connect(sigc::mem_fun(this, &PatchTreeWindow::new_object)); +} + + +void +PatchTreeWindow::new_object(SharedPtr<ObjectModel> object) +{ + SharedPtr<PatchModel> patch = PtrCast<PatchModel>(object); + if (patch) + add_patch(patch); +} + + +void +PatchTreeWindow::add_patch(SharedPtr<PatchModel> pm) +{ + if (!pm->parent()) { + Gtk::TreeModel::iterator iter = _patch_treestore->append(); + Gtk::TreeModel::Row row = *iter; + if (pm->path() == "/") { + SharedPtr<OSCEngineSender> osc_sender = PtrCast<OSCEngineSender>(App::instance().engine()); + string root_name = osc_sender ? osc_sender->engine_url() : "Internal"; + // Hack off trailing '/' if it's there (ugly) + //if (root_name.substr(root_name.length()-1,1) == "/") + // root_name = root_name.substr(0, root_name.length()-1); + //root_name.append(":/"); + row[_patch_tree_columns.name_col] = root_name; + } else { + row[_patch_tree_columns.name_col] = pm->path().name(); + } + row[_patch_tree_columns.enabled_col] = false; + row[_patch_tree_columns.patch_model_col] = pm; + _patches_treeview->expand_row(_patch_treestore->get_path(iter), true); + } else { + Gtk::TreeModel::Children children = _patch_treestore->children(); + Gtk::TreeModel::iterator c = find_patch(children, pm->parent()->path()); + + if (c != children.end()) { + Gtk::TreeModel::iterator iter = _patch_treestore->append(c->children()); + Gtk::TreeModel::Row row = *iter; + row[_patch_tree_columns.name_col] = pm->path().name(); + row[_patch_tree_columns.enabled_col] = false; + row[_patch_tree_columns.patch_model_col] = pm; + _patches_treeview->expand_row(_patch_treestore->get_path(iter), true); + } + } + + pm->enabled_sig.connect(sigc::bind(sigc::mem_fun(this, &PatchTreeWindow::patch_enabled), pm->path())); + pm->disabled_sig.connect(sigc::bind(sigc::mem_fun(this, &PatchTreeWindow::patch_disabled), pm->path())); +} + + +void +PatchTreeWindow::remove_patch(const Path& path) +{ + Gtk::TreeModel::iterator i = find_patch(_patch_treestore->children(), path); + if (i != _patch_treestore->children().end()) + _patch_treestore->erase(i); +} + + +Gtk::TreeModel::iterator +PatchTreeWindow::find_patch(Gtk::TreeModel::Children root, const Path& path) +{ + for (Gtk::TreeModel::iterator c = root.begin(); c != root.end(); ++c) { + SharedPtr<PatchModel> pm = (*c)[_patch_tree_columns.patch_model_col]; + if (pm->path() == path) { + return c; + } else if ((*c)->children().size() > 0) { + Gtk::TreeModel::iterator ret = find_patch(c->children(), path); + if (ret != c->children().end()) + return ret; + } + } + return root.end(); +} + +/* +void +PatchTreeWindow::event_patch_selected() +{ + Gtk::TreeModel::iterator active = _patch_tree_selection->get_selected(); + if (active) { + Gtk::TreeModel::Row row = *active; + SharedPtr<PatchModel> pm = row[_patch_tree_columns.patch_model_col]; + } +} +*/ + + +/** Show the context menu for the selected patch in the patches treeview. + */ +void +PatchTreeWindow::show_patch_menu(GdkEventButton* ev) +{ + Gtk::TreeModel::iterator active = _patch_tree_selection->get_selected(); + if (active) { + Gtk::TreeModel::Row row = *active; + SharedPtr<PatchModel> pm = row[_patch_tree_columns.patch_model_col]; + if (pm) + cerr << "FIXME: patch menu\n"; + //pm->show_menu(ev); + } +} + + +void +PatchTreeWindow::event_patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeView::Column* col) +{ + Gtk::TreeModel::iterator active = _patch_treestore->get_iter(path); + Gtk::TreeModel::Row row = *active; + SharedPtr<PatchModel> pm = row[_patch_tree_columns.patch_model_col]; + + App::instance().window_factory()->present_patch(pm); +} + + +void +PatchTreeWindow::event_patch_enabled_toggled(const Glib::ustring& path_str) +{ + Gtk::TreeModel::Path path(path_str); + Gtk::TreeModel::iterator active = _patch_treestore->get_iter(path); + Gtk::TreeModel::Row row = *active; + + SharedPtr<PatchModel> pm = row[_patch_tree_columns.patch_model_col]; + Glib::ustring patch_path = pm->path(); + + assert(pm); + + if ( ! pm->enabled()) { + if (_enable_signal) + App::instance().engine()->enable_patch(patch_path); + //row[_patch_tree_columns.enabled_col] = true; + } else { + if (_enable_signal) + App::instance().engine()->disable_patch(patch_path); + //row[_patch_tree_columns.enabled_col] = false; + } +} + + +void +PatchTreeWindow::patch_enabled(const Path& path) +{ + _enable_signal = false; + + Gtk::TreeModel::iterator i + = find_patch(_patch_treestore->children(), path); + + if (i != _patch_treestore->children().end()) { + Gtk::TreeModel::Row row = *i; + row[_patch_tree_columns.enabled_col] = true; + } else { + cerr << "[PatchTreeWindow] Unable to find patch " << path << endl; + } + + _enable_signal = true; +} + + +void +PatchTreeWindow::patch_disabled(const Path& path) +{ + _enable_signal = false; + + Gtk::TreeModel::iterator i + = find_patch(_patch_treestore->children(), path); + + if (i != _patch_treestore->children().end()) { + Gtk::TreeModel::Row row = *i; + row[_patch_tree_columns.enabled_col] = false; + } else { + cerr << "[PatchTreeWindow] Unable to find patch " << path << endl; + } + + _enable_signal = true; +} + + +void +PatchTreeWindow::patch_renamed(const Path& old_path, const Path& new_path) +{ + _enable_signal = false; + + Gtk::TreeModel::iterator i + = find_patch(_patch_treestore->children(), old_path); + + if (i != _patch_treestore->children().end()) { + Gtk::TreeModel::Row row = *i; + row[_patch_tree_columns.name_col] = new_path.name(); + } else { + cerr << "[PatchTreeWindow] Unable to find patch " << old_path << endl; + } + + _enable_signal = true; +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/PatchTreeWindow.h b/src/libs/gui/PatchTreeWindow.h new file mode 100644 index 00000000..9868d363 --- /dev/null +++ b/src/libs/gui/PatchTreeWindow.h @@ -0,0 +1,112 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHTREEWINDOW_H +#define PATCHTREEWINDOW_H + +#include <gtkmm.h> +#include <libglademm.h> +#include <raul/Path.h> + +namespace Ingen { namespace Client { + class Store; +} } +using Ingen::Client::Store; + +namespace Ingen { +namespace GUI { + +class PatchWindow; +class PatchTreeView; + + +/** Window with a TreeView of all loaded patches. + * + * \ingroup GUI + */ +class PatchTreeWindow : public Gtk::Window +{ +public: + PatchTreeWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); + + void init(Store& store); + + void new_object(SharedPtr<ObjectModel> object); + + void patch_enabled(const Path& path); + void patch_disabled(const Path& path); + void patch_renamed(const Path& old_path, const Path& new_path); + + void add_patch(SharedPtr<PatchModel> pm); + void remove_patch(const Path& path); + void show_patch_menu(GdkEventButton* ev); + +protected: + //void event_patch_selected(); + void event_patch_activated(const Gtk::TreeModel::Path& path, Gtk::TreeView::Column* col); + void event_patch_enabled_toggled(const Glib::ustring& path_str); + + Gtk::TreeModel::iterator find_patch(Gtk::TreeModel::Children root, const Path& path); + + PatchTreeView* _patches_treeview; + + struct PatchTreeModelColumns : public Gtk::TreeModel::ColumnRecord + { + PatchTreeModelColumns() + { add(name_col); add(enabled_col); add(patch_model_col); } + + Gtk::TreeModelColumn<Glib::ustring> name_col; + Gtk::TreeModelColumn<bool> enabled_col; + Gtk::TreeModelColumn<SharedPtr<PatchModel> > patch_model_col; + }; + + bool _enable_signal; + PatchTreeModelColumns _patch_tree_columns; + Glib::RefPtr<Gtk::TreeStore> _patch_treestore; + Glib::RefPtr<Gtk::TreeSelection> _patch_tree_selection; +}; + + +/** Derived TreeView class to support context menus for patches */ +class PatchTreeView : public Gtk::TreeView +{ +public: + PatchTreeView(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Gtk::TreeView(cobject) + {} + + void set_window(PatchTreeWindow* win) { _window = win; } + + bool on_button_press_event(GdkEventButton* ev) { + bool ret = Gtk::TreeView::on_button_press_event(ev); + + if ((ev->type == GDK_BUTTON_PRESS) && (ev->button == 3)) + _window->show_patch_menu(ev); + + return ret; + } + +private: + PatchTreeWindow* _window; + +}; // struct PatchTreeView + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHTREEWINDOW_H diff --git a/src/libs/gui/PatchView.cpp b/src/libs/gui/PatchView.cpp new file mode 100644 index 00000000..7b12fe40 --- /dev/null +++ b/src/libs/gui/PatchView.cpp @@ -0,0 +1,162 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <iostream> +#include <cassert> +#include <fstream> +#include "interface/EngineInterface.h" +#include "client/PatchModel.h" +#include "App.h" +#include "PatchView.h" +#include "PatchCanvas.h" +#include "LoadPluginWindow.h" +#include "NewSubpatchWindow.h" +#include "LoadSubpatchWindow.h" +#include "NodeControlWindow.h" +#include "PatchPropertiesWindow.h" +#include "PatchTreeWindow.h" +#include "GladeFactory.h" + +namespace Ingen { +namespace GUI { + + +PatchView::PatchView(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: Gtk::Box(cobject), + _breadcrumb_container(NULL), + _enable_signal(true) +{ + property_visible() = false; + + xml->get_widget("patch_view_breadcrumb_container", _breadcrumb_container); + xml->get_widget("patch_view_process_but", _process_but); + xml->get_widget("patch_view_poly_spin", _poly_spin); + xml->get_widget("patch_view_clear_but", _clear_but); + xml->get_widget("patch_view_destroy_but", _destroy_but); + xml->get_widget("patch_view_refresh_but", _refresh_but); + xml->get_widget("patch_view_save_but", _save_but); + xml->get_widget("patch_view_zoom_full_but", _zoom_full_but); + xml->get_widget("patch_view_zoom_normal_but", _zoom_normal_but); + xml->get_widget("patch_view_scrolledwindow", _canvas_scrolledwindow); +} + + +void +PatchView::set_patch(SharedPtr<PatchModel> patch) +{ + assert(!_canvas); // FIXME: remove + + cerr << "Creating view for " << patch->path() << endl; + + assert(_breadcrumb_container); // ensure created + + _patch = patch; + _canvas = SharedPtr<PatchCanvas>(new PatchCanvas(patch, 1600*2, 1200*2)); + _canvas->build(); + + _canvas_scrolledwindow->add(*_canvas); + + _poly_spin->set_value(patch->poly()); + _destroy_but->set_sensitive(patch->path() != "/"); + patch->enabled() ? enable() : disable(); + + // Connect model signals to track state + patch->enabled_sig.connect(sigc::mem_fun(this, &PatchView::enable)); + patch->disabled_sig.connect(sigc::mem_fun(this, &PatchView::disable)); + + // Connect widget signals to do things + _process_but->signal_toggled().connect(sigc::mem_fun(this, &PatchView::process_toggled)); + _clear_but->signal_clicked().connect(sigc::mem_fun(this, &PatchView::clear_clicked)); + _refresh_but->signal_clicked().connect(sigc::mem_fun(this, &PatchView::refresh_clicked)); + + _zoom_normal_but->signal_clicked().connect(sigc::bind(sigc::mem_fun( + _canvas.get(), &FlowCanvas::set_zoom), 1.0)); + + _zoom_full_but->signal_clicked().connect( + sigc::mem_fun(_canvas.get(), &FlowCanvas::zoom_full)); +} + + +PatchView::~PatchView() +{ + cerr << "Destroying view for " << _patch->path() << endl; +} + + +SharedPtr<PatchView> +PatchView::create(SharedPtr<PatchModel> patch) + +{ + const Glib::RefPtr<Gnome::Glade::Xml>& xml = GladeFactory::new_glade_reference("patch_view_box"); + PatchView* result = NULL; + xml->get_widget_derived("patch_view_box", result); + assert(result); + result->set_patch(patch); + return SharedPtr<PatchView>(result); +} + + +void +PatchView::process_toggled() +{ + if (!_enable_signal) + return; + + if (_process_but->get_active()) { + App::instance().engine()->enable_patch(_patch->path()); + App::instance().patch_tree()->patch_enabled(_patch->path()); + } else { + App::instance().engine()->disable_patch(_patch->path()); + App::instance().patch_tree()->patch_disabled(_patch->path()); + } +} + + +void +PatchView::clear_clicked() +{ + App::instance().engine()->clear_patch(_patch->path()); +} + + +void +PatchView::refresh_clicked() +{ + App::instance().engine()->request_object(_patch->path()); +} + + +void +PatchView::enable() +{ + _enable_signal = false; + _process_but->set_active(true); + _enable_signal = true; +} + + +void +PatchView::disable() +{ + _enable_signal = false; + _process_but->set_active(false); + _enable_signal = true; +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/PatchView.h b/src/libs/gui/PatchView.h new file mode 100644 index 00000000..72d773fa --- /dev/null +++ b/src/libs/gui/PatchView.h @@ -0,0 +1,102 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHVIEW_H +#define PATCHVIEW_H + +#include <string> +#include <gtkmm.h> +#include <libglademm/xml.h> +#include <libglademm.h> +#include <raul/SharedPtr.h> +#include "client/PatchModel.h" + +using std::string; + +namespace Ingen { namespace Client { + class PortModel; + class ControlModel; + class MetadataModel; +} } +using namespace Ingen::Client; + + +namespace Ingen { +namespace GUI { + +class PatchCanvas; +class LoadPluginWindow; +class NewSubpatchWindow; +class LoadSubpatchWindow; +class NewSubpatchWindow; +class NodeControlWindow; +class PatchDescriptionWindow; +class SubpatchModule; +class OmPort; + + +/** The patch specific contents of a PatchWindow (ie the canvas and whatever else). + * + * \ingroup GUI + */ +class PatchView : public Gtk::Box +{ +public: + PatchView(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml); + ~PatchView(); + + SharedPtr<PatchCanvas> canvas() const { return _canvas; } + SharedPtr<PatchModel> patch() const { return _patch; } + Gtk::Viewport* breadcrumb_container() const { return _breadcrumb_container; } + + static SharedPtr<PatchView> create(SharedPtr<PatchModel> patch); + +private: + void set_patch(SharedPtr<PatchModel> patch); + + void process_toggled(); + void clear_clicked(); + void refresh_clicked(); + + void enable(); + void disable(); + + void zoom_full(); + + SharedPtr<PatchModel> _patch; + SharedPtr<PatchCanvas> _canvas; + + Gtk::ScrolledWindow* _canvas_scrolledwindow; + + Gtk::ToggleToolButton* _process_but; + Gtk::SpinButton* _poly_spin; + Gtk::ToolButton* _clear_but; + Gtk::ToolButton* _destroy_but; + Gtk::ToolButton* _refresh_but; + Gtk::ToolButton* _save_but; + Gtk::ToolButton* _zoom_normal_but; + Gtk::ToolButton* _zoom_full_but; + Gtk::Viewport* _breadcrumb_container; + + bool _enable_signal; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHVIEW_H diff --git a/src/libs/gui/PatchWindow.cpp b/src/libs/gui/PatchWindow.cpp new file mode 100644 index 00000000..ff2a6a4a --- /dev/null +++ b/src/libs/gui/PatchWindow.cpp @@ -0,0 +1,467 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "PatchWindow.h" +#include <iostream> +#include <cassert> +#include <fstream> +#include "interface/EngineInterface.h" +#include "client/PatchModel.h" +#include "client/Store.h" +#include "App.h" +#include "PatchCanvas.h" +#include "LoadPluginWindow.h" +#include "NewSubpatchWindow.h" +#include "LoadPatchWindow.h" +#include "LoadSubpatchWindow.h" +#include "NodeControlWindow.h" +#include "PatchPropertiesWindow.h" +#include "ConfigWindow.h" +#include "MessagesWindow.h" +#include "PatchTreeWindow.h" +#include "BreadCrumbBox.h" +#include "ConnectWindow.h" +#include "ThreadedLoader.h" +#include "WindowFactory.h" +#include "PatchView.h" + +namespace Ingen { +namespace GUI { + + +PatchWindow::PatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) +: Gtk::Window(cobject), + _enable_signal(true), + _position_stored(false), + _x(0), + _y(0), + _breadcrumb_box(NULL) +{ + property_visible() = false; + + xml->get_widget("patch_win_vbox", _vbox); + xml->get_widget("patch_win_viewport", _viewport); + //xml->get_widget("patch_win_status_bar", _status_bar); + //xml->get_widget("patch_open_menuitem", _menu_open); + xml->get_widget("patch_import_menuitem", _menu_import); + xml->get_widget("patch_import_location_menuitem", _menu_import_location); + //xml->get_widget("patch_open_into_menuitem", _menu_open_into); + xml->get_widget("patch_save_menuitem", _menu_save); + xml->get_widget("patch_save_as_menuitem", _menu_save_as); + xml->get_widget("patch_upload_menuitem", _menu_upload); + xml->get_widget("patch_cut_menuitem", _menu_cut); + xml->get_widget("patch_copy_menuitem", _menu_copy); + xml->get_widget("patch_paste_menuitem", _menu_paste); + xml->get_widget("patch_delete_menuitem", _menu_delete); + xml->get_widget("patch_close_menuitem", _menu_close); + xml->get_widget("patch_configuration_menuitem", _menu_configuration); + xml->get_widget("patch_quit_menuitem", _menu_quit); + xml->get_widget("patch_view_control_window_menuitem", _menu_view_control_window); + xml->get_widget("patch_view_engine_window_menuitem", _menu_view_engine_window); + xml->get_widget("patch_properties_menuitem", _menu_view_patch_properties); + xml->get_widget("patch_fullscreen_menuitem", _menu_fullscreen); + xml->get_widget("patch_arrange_menuitem", _menu_arrange); + xml->get_widget("patch_clear_menuitem", _menu_clear); + xml->get_widget("patch_destroy_menuitem", _menu_destroy_patch); + xml->get_widget("patch_view_messages_window_menuitem", _menu_view_messages_window); + xml->get_widget("patch_view_patch_tree_window_menuitem", _menu_view_patch_tree_window); + xml->get_widget("patch_help_about_menuitem", _menu_help_about); + + _menu_view_control_window->property_sensitive() = false; + //m_status_bar->push(App::instance().engine()->engine_url()); + //m_status_bar->pack_start(*Gtk::manage(new Gtk::Image(Gtk::Stock::CONNECT, Gtk::ICON_SIZE_MENU)), false, false); + + /*_menu_open->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_open));*/ + _menu_import->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_import)); + _menu_import_location->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_import_location)); + _menu_save->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_save)); + _menu_save_as->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_save_as)); + _menu_upload->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_upload)); + _menu_copy->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_copy)); + _menu_delete->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_delete)); + _menu_quit->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_quit)); + _menu_configuration->signal_activate().connect( + sigc::mem_fun(App::instance().configuration_dialog(), &ConfigWindow::show)); + _menu_fullscreen->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_fullscreen_toggled)); + _menu_arrange->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_arrange)); + _menu_view_engine_window->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_show_engine)); + _menu_view_control_window->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_show_controls)); + _menu_view_patch_properties->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_show_properties)); + _menu_destroy_patch->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_destroy)); + _menu_clear->signal_activate().connect( + sigc::mem_fun(this, &PatchWindow::event_clear)); + _menu_view_messages_window->signal_activate().connect( + sigc::mem_fun<void>(App::instance().messages_dialog(), &MessagesWindow::present)); + _menu_view_patch_tree_window->signal_activate().connect( + sigc::mem_fun<void>(App::instance().patch_tree(), &PatchTreeWindow::present)); + + _menu_help_about->signal_activate().connect( + sigc::mem_fun<void>(App::instance().about_dialog(), &Gtk::Dialog::present)); + + _breadcrumb_box = new BreadCrumbBox(); + _breadcrumb_box->signal_patch_selected.connect(sigc::mem_fun(this, &PatchWindow::set_patch_from_path)); +} + + +PatchWindow::~PatchWindow() +{ + // Prevents deletion + //m_patch->claim_patch_view(); + + delete _breadcrumb_box; +} + + +/** Set the patch controller from a Path (for use by eg. BreadCrumbBox) + */ +void +PatchWindow::set_patch_from_path(const Path& path, SharedPtr<PatchView> view) +{ + if (view) { + assert(view->patch()->path() == path); + App::instance().window_factory()->present_patch(view->patch(), this, view); + } else { + SharedPtr<PatchModel> model = PtrCast<PatchModel>(App::instance().store()->object(path)); + if (model) + App::instance().window_factory()->present_patch(model, this); + } +} + + +/** Sets the patch controller for this window and initializes everything. + * + * If @a view is NULL, a new view will be created. + */ +void +PatchWindow::set_patch(SharedPtr<PatchModel> patch, SharedPtr<PatchView> view) +{ + if (!patch || patch == _patch) + return; + + _enable_signal = false; + + _patch = patch; + + _view = view; + + if (!_view) + _view = _breadcrumb_box->view(patch->path()); + + if (!_view) + _view = PatchView::create(patch); + + assert(_view); + + // Add view to our viewport + if (_view->get_parent()) + _view->get_parent()->remove(*_view.get()); + + _viewport->remove(); + _viewport->add(*_view.get()); + + + if (_breadcrumb_box->get_parent()) + _breadcrumb_box->get_parent()->remove(*_breadcrumb_box); + + _view->breadcrumb_container()->remove(); + _view->breadcrumb_container()->add(*_breadcrumb_box); + _view->breadcrumb_container()->show(); + + _breadcrumb_box->build(patch->path(), _view); + _breadcrumb_box->show(); + + _menu_view_control_window->property_sensitive() = false; + + for (PortModelList::const_iterator p = patch->ports().begin(); + p != patch->ports().end(); ++p) { + if ((*p)->is_control()) { + _menu_view_control_window->property_sensitive() = true; + break; + } + } + + int width, height; + get_size(width, height); + _view->canvas()->scroll_to( + ((int)_view->canvas()->width() - width)/2, + ((int)_view->canvas()->height() - height)/2); + + set_title(_patch->path() + " - Ingenuity"); + + //m_properties_window->patch_model(pc->patch_model()); + + if (patch->path() == "/") + _menu_destroy_patch->set_sensitive(false); + else + _menu_destroy_patch->set_sensitive(true); + + show_all(); + + _enable_signal = true; +} + + +void +PatchWindow::event_show_engine() +{ + if (_patch) + App::instance().connect_window()->show(); +} + + +void +PatchWindow::event_show_controls() +{ + App::instance().window_factory()->present_controls(_patch); +} + + +void +PatchWindow::event_show_properties() +{ + App::instance().window_factory()->present_properties(_patch); +} + + +void +PatchWindow::event_import() +{ + App::instance().window_factory()->present_load_patch(_patch); +} + + +void +PatchWindow::event_import_location() +{ + App::instance().window_factory()->present_load_remote_patch(_patch); +} + + +void +PatchWindow::event_save() +{ + if (_patch->filename() == "") + event_save_as(); + else + App::instance().loader()->save_patch(_patch, _patch->filename(), false); +} + + +void +PatchWindow::event_save_as() +{ + Gtk::FileChooserDialog dialog(*this, "Save Patch", Gtk::FILE_CHOOSER_ACTION_SAVE); + + /*Gtk::VBox* box = dialog.get_vbox(); + Gtk::Label warning("Warning: Recursively saving will overwrite any subpatch files \ + without confirmation."); + box->pack_start(warning, false, false, 2); + Gtk::CheckButton recursive_checkbutton("Recursively save all subpatches"); + box->pack_start(recursive_checkbutton, false, false, 0); + recursive_checkbutton.show(); + */ + + dialog.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + Gtk::Button* save_button = dialog.add_button(Gtk::Stock::SAVE, Gtk::RESPONSE_OK); + save_button->property_has_default() = true; + + // Set current folder to most sensible default + const string& current_filename = _patch->filename(); + if (current_filename.length() > 0) + dialog.set_filename(current_filename); + else if (App::instance().configuration()->patch_folder().length() > 0) + dialog.set_current_folder(App::instance().configuration()->patch_folder()); + + int result = dialog.run(); + //bool recursive = recursive_checkbutton.get_active(); + + assert(result == Gtk::RESPONSE_OK || result == Gtk::RESPONSE_CANCEL || result == Gtk::RESPONSE_NONE); + + if (result == Gtk::RESPONSE_OK) { + string filename = dialog.get_filename(); + if (filename.length() < 11 || filename.substr(filename.length()-10) != ".ingen.ttl") + filename += ".ingen.ttl"; + + bool confirm = false; + std::fstream fin; + fin.open(filename.c_str(), std::ios::in); + if (fin.is_open()) { // File exists + string msg = "File already exists! Are you sure you want to overwrite "; + msg += filename + "?"; + Gtk::MessageDialog confirm_dialog(*this, + msg, false, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_YES_NO, true); + if (confirm_dialog.run() == Gtk::RESPONSE_YES) + confirm = true; + else + confirm = false; + } else { // File doesn't exist + confirm = true; + } + fin.close(); + + if (confirm) { + App::instance().loader()->save_patch(_patch, filename, true); + _patch->set_filename(filename); + //_patch->set_metadata("filename", Atom(filename.c_str())); + } + } + App::instance().configuration()->set_patch_folder(dialog.get_current_folder()); +} + + +void +PatchWindow::event_upload() +{ + App::instance().window_factory()->present_upload_patch(_patch); +} + + +void +PatchWindow::event_copy() +{ + if (_view) + _view->canvas()->copy_selection(); +} + + +void +PatchWindow::event_delete() +{ + if (_view) + _view->canvas()->destroy_selection(); +} + + +void +PatchWindow::on_show() +{ + if (_position_stored) + move(_x, _y); + + Gtk::Window::on_show(); +} + + +void +PatchWindow::on_hide() +{ + _position_stored = true; + get_position(_x, _y); + Gtk::Window::on_hide(); +} + + +bool +PatchWindow::on_key_press_event(GdkEventKey* event) +{ + if (event->keyval == GDK_Delete) { + cerr << "FIXME: delete key\n"; + /* + if (_patch && _patch->get_view()) { + assert(_patch->get_view()->canvas()); + _patch->get_view()->canvas()->destroy_selected(); + }*/ + return true; + } else { + return Gtk::Window::on_key_press_event(event); + } +} + + +void +PatchWindow::event_quit() +{ + Gtk::MessageDialog d(*this, "Would you like to quit just Ingenuity\nor kill the engine as well?", + true, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_NONE, true); + d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + + Gtk::Button* b = d.add_button(Gtk::Stock::REMOVE, 2); // kill + b->set_label("_Kill Engine"); + Gtk::Widget* kill_img = Gtk::manage(new Gtk::Image(Gtk::Stock::CLOSE, Gtk::ICON_SIZE_BUTTON)); + b->set_image(*kill_img); + + b = d.add_button(Gtk::Stock::QUIT, 1); // just exit + b->set_label("_Quit"); + Gtk::Widget* close_img = Gtk::manage(new Gtk::Image(Gtk::Stock::QUIT, Gtk::ICON_SIZE_BUTTON)); + b->set_image(*close_img); + b->grab_default(); + + int ret = d.run(); + if (ret == 1) { + App::instance().quit(); + } else if (ret == 2) { + App::instance().engine()->quit(); + App::instance().quit(); + } + // Otherwise cancelled, do nothing +} + + +void +PatchWindow::event_destroy() +{ + App::instance().engine()->destroy(_patch->path()); +} + + +void +PatchWindow::event_clear() +{ + App::instance().engine()->clear_patch(_patch->path()); +} + + +void +PatchWindow::event_arrange() +{ + _view->canvas()->arrange(); +} + + +void +PatchWindow::event_fullscreen_toggled() +{ + // FIXME: ugh, use GTK signals to track state and know for sure + static bool is_fullscreen = false; + + if (!is_fullscreen) { + fullscreen(); + is_fullscreen = true; + } else { + unfullscreen(); + is_fullscreen = false; + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/PatchWindow.h b/src/libs/gui/PatchWindow.h new file mode 100644 index 00000000..6e95c3d8 --- /dev/null +++ b/src/libs/gui/PatchWindow.h @@ -0,0 +1,143 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHWINDOW_H +#define PATCHWINDOW_H + +#include <string> +#include <list> +#include <gtkmm.h> +#include <libglademm/xml.h> +#include <libglademm.h> +#include <raul/Path.h> +#include <raul/SharedPtr.h> +#include "client/PatchModel.h" +#include "PatchView.h" +using Ingen::Client::PatchModel; + +using std::string; using std::list; + + +namespace Ingen { namespace Client { + class PatchModel; + class PortModel; + class ControlModel; + class MetadataModel; +} } +using namespace Ingen::Client; + + +namespace Ingen { +namespace GUI { + +class LoadPluginWindow; +class LoadPatchWindow; +class NewSubpatchWindow; +class LoadSubpatchWindow; +class NewSubpatchWindow; +class NodeControlWindow; +class PatchDescriptionWindow; +class SubpatchModule; +class OmPort; +class BreadCrumbBox; + + +/** A window for a patch. + * + * \ingroup GUI + */ +class PatchWindow : public Gtk::Window +{ +public: + PatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml); + ~PatchWindow(); + + void set_patch_from_path(const Path& path, SharedPtr<PatchView> view); + void set_patch(SharedPtr<PatchModel> pc, SharedPtr<PatchView> view); + + SharedPtr<PatchModel> patch() const { return _patch; } + + Gtk::MenuItem* menu_view_control_window() { return _menu_view_control_window; } + +protected: + void on_show(); + void on_hide(); + bool on_key_press_event(GdkEventKey* event); + +private: + void event_import(); + void event_import_location(); + void event_save(); + void event_save_as(); + void event_upload(); + void event_copy(); + void event_delete(); + void event_quit(); + void event_destroy(); + void event_clear(); + void event_fullscreen_toggled(); + void event_arrange(); + void event_show_properties(); + void event_show_controls(); + void event_show_engine(); + + SharedPtr<PatchModel> _patch; + SharedPtr<PatchView> _view; + + bool _enable_signal; + bool _position_stored; + int _x; + int _y; + + Gtk::MenuItem* _menu_import; + Gtk::MenuItem* _menu_import_location; + Gtk::MenuItem* _menu_save; + Gtk::MenuItem* _menu_save_as; + Gtk::MenuItem* _menu_upload; + Gtk::MenuItem* _menu_cut; + Gtk::MenuItem* _menu_copy; + Gtk::MenuItem* _menu_paste; + Gtk::MenuItem* _menu_delete; + Gtk::MenuItem* _menu_configuration; + Gtk::MenuItem* _menu_close; + Gtk::MenuItem* _menu_quit; + Gtk::MenuItem* _menu_fullscreen; + Gtk::MenuItem* _menu_clear; + Gtk::MenuItem* _menu_destroy_patch; + Gtk::MenuItem* _menu_arrange; + Gtk::MenuItem* _menu_view_engine_window; + Gtk::MenuItem* _menu_view_control_window; + Gtk::MenuItem* _menu_view_patch_properties; + Gtk::MenuItem* _menu_view_messages_window; + Gtk::MenuItem* _menu_view_patch_tree_window; + Gtk::MenuItem* _menu_help_about; + + Gtk::VBox* _vbox; + Gtk::Viewport* _viewport; + BreadCrumbBox* _breadcrumb_box; + + //Gtk::Statusbar* _status_bar; + + /** Invisible bin used to store breadcrumbs when not shown by a view */ + Gtk::Alignment _breadcrumb_bin; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PATCHWINDOW_H diff --git a/src/libs/gui/Port.cpp b/src/libs/gui/Port.cpp new file mode 100644 index 00000000..9b21b8be --- /dev/null +++ b/src/libs/gui/Port.cpp @@ -0,0 +1,61 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cassert> +#include <iostream> +#include "interface/EngineInterface.h" +#include "client/PatchModel.h" +#include "client/PortModel.h" +#include "client/ControlModel.h" +#include "Configuration.h" +#include "App.h" +#include "Port.h" +using std::cerr; using std::endl; + +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + + +/** @param flip Make an input port appear as an output port, and vice versa. + */ +Port::Port(boost::shared_ptr<LibFlowCanvas::Module> module, SharedPtr<PortModel> pm, bool flip, bool destroyable) +: LibFlowCanvas::Port(module, + pm->path().name(), + flip ? (!pm->is_input()) : pm->is_input(), + App::instance().configuration()->get_port_color(pm.get())), + _port_model(pm) +{ + assert(module); + assert(_port_model); + + if (destroyable) + _menu.items().push_back(Gtk::Menu_Helpers::MenuElem("Destroy", + sigc::mem_fun(this, &Port::on_menu_destroy))); +} + + +void +Port::on_menu_destroy() +{ + App::instance().engine()->destroy(_port_model->path()); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/Port.h b/src/libs/gui/Port.h new file mode 100644 index 00000000..b798cbca --- /dev/null +++ b/src/libs/gui/Port.h @@ -0,0 +1,57 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PORT_H +#define PORT_H + +#include <cassert> +#include <string> +#include <flowcanvas/Port.h> +#include <raul/SharedPtr.h> + +namespace Ingen { namespace Client { class PortModel; } } +using Ingen::Client::PortModel; + +namespace Ingen { +namespace GUI { + + +/** A Port on an Module. + * + * \ingroup GUI + */ +class Port : public LibFlowCanvas::Port +{ +public: + Port(boost::shared_ptr<LibFlowCanvas::Module> module, SharedPtr<PortModel> pm, bool flip = false, bool destroyable = false); + + virtual ~Port() {} + + SharedPtr<PortModel> model() const { return _port_model; } + +private: + + void on_menu_destroy(); + + SharedPtr<PortModel> _port_model; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // PORT_H diff --git a/src/libs/gui/PortPropertiesWindow.cpp b/src/libs/gui/PortPropertiesWindow.cpp new file mode 100644 index 00000000..10b65641 --- /dev/null +++ b/src/libs/gui/PortPropertiesWindow.cpp @@ -0,0 +1,175 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cassert> +#include <string> +#include "interface/EngineInterface.h" +#include "client/NodeModel.h" +#include "client/PluginModel.h" +#include "App.h" +#include "ControlGroups.h" +#include "PortPropertiesWindow.h" + +using std::string; + +namespace Ingen { +namespace GUI { + + +PortPropertiesWindow::PortPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Gtk::Dialog(cobject) + , _enable_signal(false) + , _control(NULL) +{ + xml->get_widget("port_properties_min_spinner", _min_spinner); + xml->get_widget("port_properties_max_spinner", _max_spinner); + xml->get_widget("port_properties_cancel_button", _cancel_button); + xml->get_widget("port_properties_ok_button", _ok_button); + + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, + &PortPropertiesWindow::cancel)); + + _ok_button->signal_clicked().connect(sigc::mem_fun(this, + &PortPropertiesWindow::ok)); +} + + +/** Set the port this window is associated with. + * This function MUST be called before using this object in any way. + */ +void +PortPropertiesWindow::init(ControlGroup* control, SharedPtr<PortModel> pm) +{ + assert(pm); + assert(control); + + _port_model = pm; + _control = control; + + + set_title(pm->path() + " Properties"); + + // FIXME: code duplication w/ ControlGroups.cpp + float min = 0.0f; + float max = 1.0f; + + const Atom& min_atom = pm->get_metadata("ingen:minimum"); + const Atom& max_atom = pm->get_metadata("ingen_maximum"); + if (min_atom.type() == Atom::FLOAT && max_atom.type() == Atom::FLOAT) { + min = min_atom.get_float(); + max = max_atom.get_float(); + } + + const SharedPtr<NodeModel> parent = PtrCast<NodeModel>(pm->parent()); + + if (parent && parent->plugin() && parent->plugin()->type() == PluginModel::LV2) { + min = slv2_port_get_minimum_value( + parent->plugin()->slv2_plugin(), + slv2_plugin_get_port_by_symbol(parent->plugin()->slv2_plugin(), + pm->path().name().c_str())); + max = slv2_port_get_maximum_value( + parent->plugin()->slv2_plugin(), + slv2_plugin_get_port_by_symbol(parent->plugin()->slv2_plugin(), + pm->path().name().c_str())); + } + + if (max <= min) + max = min + 1.0f; + + _initial_min = min; + _initial_max = max; + + _min_spinner->set_value(min); + _min_spinner->signal_value_changed().connect(sigc::mem_fun(*this, &PortPropertiesWindow::min_changed)); + _max_spinner->set_value(max); + _max_spinner->signal_value_changed().connect(sigc::mem_fun(*this, &PortPropertiesWindow::max_changed)); + + pm->metadata_update_sig.connect(sigc::mem_fun(this, &PortPropertiesWindow::metadata_update)); + + _enable_signal = true; +} + + +void +PortPropertiesWindow::metadata_update(const string& key, const Atom& value) +{ + _enable_signal = false; + + if ( (key == "ingen:minimum") && value.type() == Atom::FLOAT) + _min_spinner->set_value(value.get_float()); + else if ( (key == "ingen:maximum") && value.type() == Atom::FLOAT) + _max_spinner->set_value(value.get_float()); + + _enable_signal = true; +} + + +void +PortPropertiesWindow::min_changed() +{ + float min = _min_spinner->get_value(); + const float max = _max_spinner->get_value(); + + if (min >= max) { + min = max - 1.0; + _min_spinner->set_value(min); + } + + _control->set_range(min, max); + + if (_enable_signal) + App::instance().engine()->set_metadata(_port_model->path(), "ingen:minimum", min); +} + + +void +PortPropertiesWindow::max_changed() +{ + const float min = _min_spinner->get_value(); + float max = _max_spinner->get_value(); + + if (max <= min) { + max = min + 1.0; + _max_spinner->set_value(max); + } + + _control->set_range(min, max); + + if (_enable_signal) + App::instance().engine()->set_metadata(_port_model->path(), "ingen:maximum", max); +} + + +void +PortPropertiesWindow::cancel() +{ + App::instance().engine()->set_metadata(_port_model->path(), "ingen:minimum", _initial_min); + App::instance().engine()->set_metadata(_port_model->path(), "ingen:maximum", _initial_max); + delete this; +} + + +void +PortPropertiesWindow::ok() +{ + delete this; +} + + +} // namespace GUI +} // namespace Ingen + diff --git a/src/libs/gui/PortPropertiesWindow.h b/src/libs/gui/PortPropertiesWindow.h new file mode 100644 index 00000000..b7900bc6 --- /dev/null +++ b/src/libs/gui/PortPropertiesWindow.h @@ -0,0 +1,70 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PORTPROPERTIESWINDOW_H +#define PORTPROPERTIESWINDOW_H + +#include <gtkmm.h> +#include <libglademm.h> +#include <raul/SharedPtr.h> +#include "client/PortModel.h" +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class ControlGroup; + + +/** Port properties window. + * + * Loaded by libglade as a derived object. + * + * \ingroup GUI + */ +class PortPropertiesWindow : public Gtk::Dialog +{ +public: + PortPropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); + + void init(ControlGroup* control, SharedPtr<PortModel> port_model); + +private: + void metadata_update(const string& key, const Atom& value); + void min_changed(); + void max_changed(); + + void ok(); + void cancel(); + + bool _enable_signal; + + float _initial_min; + float _initial_max; + + ControlGroup* _control; + SharedPtr<PortModel> _port_model; + Gtk::SpinButton* _min_spinner; + Gtk::SpinButton* _max_spinner; + Gtk::Button* _cancel_button; + Gtk::Button* _ok_button; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // PORTPROPERTIESWINDOW_H diff --git a/src/libs/gui/RenameWindow.cpp b/src/libs/gui/RenameWindow.cpp new file mode 100644 index 00000000..d9c474a8 --- /dev/null +++ b/src/libs/gui/RenameWindow.cpp @@ -0,0 +1,117 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <cassert> +#include <string> +#include "interface/EngineInterface.h" +#include "client/ObjectModel.h" +#include "client/Store.h" +#include "App.h" +#include "RenameWindow.h" + +using std::string; + +namespace Ingen { +namespace GUI { + + +RenameWindow::RenameWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml) +: Gtk::Window(cobject) +{ + glade_xml->get_widget("rename_name_entry", _name_entry); + glade_xml->get_widget("rename_message_label", _message_label); + glade_xml->get_widget("rename_cancel_button", _cancel_button); + glade_xml->get_widget("rename_ok_button", _ok_button); + + _name_entry->signal_changed().connect(sigc::mem_fun(this, &RenameWindow::name_changed)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &RenameWindow::cancel_clicked)); + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &RenameWindow::ok_clicked)); + + _ok_button->property_sensitive() = false; +} + + +/** Set the object this window is renaming. + * This function MUST be called before using this object in any way. + */ +void +RenameWindow::set_object(SharedPtr<ObjectModel> object) +{ + _object = object; + _name_entry->set_text(object->path().name()); +} + + +/** Called every time the user types into the name input box. + * Used to display warning messages, and enable/disable the rename button. + */ +void +RenameWindow::name_changed() +{ + assert(_name_entry); + assert(_message_label); + assert(_object); + assert(_object->parent()); + + string name = _name_entry->get_text(); + if (name.find("/") != string::npos) { + _message_label->set_text("Name may not contain '/'"); + _ok_button->property_sensitive() = false; + //} else if (_object->parent()->patch_model()->get_node(name) != NULL) { + } else if (App::instance().store()->object(_object->parent()->path().base() + name)) { + _message_label->set_text("An object already exists with that name."); + _ok_button->property_sensitive() = false; + } else if (name.length() == 0) { + _message_label->set_text(""); + _ok_button->property_sensitive() = false; + } else { + _message_label->set_text(""); + _ok_button->property_sensitive() = true; + } +} + + +void +RenameWindow::cancel_clicked() +{ + cout << "cancel\n"; + _name_entry->set_text(""); + hide(); +} + + +/** Rename the object. + * + * It shouldn't be possible for this to be called with an invalid name set + * (since the Rename button should be deactivated). This is just shinification + * though - the engine will handle invalid names gracefully. + */ +void +RenameWindow::ok_clicked() +{ + string name = _name_entry->get_text(); + assert(name.length() > 0); + assert(name.find("/") == string::npos); + + App::instance().engine()->rename(_object->path(), name); + + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/RenameWindow.h b/src/libs/gui/RenameWindow.h new file mode 100644 index 00000000..56ee3d5a --- /dev/null +++ b/src/libs/gui/RenameWindow.h @@ -0,0 +1,60 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef RENAMEWINDOW_H +#define RENAMEWINDOW_H + +#include <gtkmm.h> +#include <libglademm.h> +#include <raul/SharedPtr.h> +#include "client/ObjectModel.h" +using Ingen::Client::ObjectModel; + +namespace Ingen { +namespace GUI { + + +/** Rename window. Handles renaming of any (Ingen) object. + * + * \ingroup GUI + */ +class RenameWindow : public Gtk::Window +{ +public: + RenameWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& refGlade); + + void present(SharedPtr<ObjectModel> object) { set_object(object); Gtk::Window::present(); } + +private: + void set_object(SharedPtr<ObjectModel> object); + + void name_changed(); + void cancel_clicked(); + void ok_clicked(); + + SharedPtr<ObjectModel> _object; + + Gtk::Entry* _name_entry; + Gtk::Label* _message_label; + Gtk::Button* _cancel_button; + Gtk::Button* _ok_button; +}; + +} // namespace GUI +} // namespace Ingen + +#endif // RENAMEWINDOW_H diff --git a/src/libs/gui/SubpatchModule.cpp b/src/libs/gui/SubpatchModule.cpp new file mode 100644 index 00000000..475774bc --- /dev/null +++ b/src/libs/gui/SubpatchModule.cpp @@ -0,0 +1,95 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "SubpatchModule.h" +#include <cassert> +#include <iostream> +#include "interface/EngineInterface.h" +#include "client/PatchModel.h" +#include "App.h" +#include "NodeModule.h" +#include "NodeControlWindow.h" +#include "PatchWindow.h" +#include "PatchCanvas.h" +#include "Port.h" +#include "WindowFactory.h" +using std::cerr; using std::cout; using std::endl; + +namespace Ingen { +namespace GUI { + + +SubpatchModule::SubpatchModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PatchModel> patch) +: NodeModule(canvas, patch), + _patch(patch) +{ + assert(canvas); + assert(patch); +} + + +void +SubpatchModule::on_double_click(GdkEventButton* event) +{ + assert(_patch); + + SharedPtr<PatchModel> parent = PtrCast<PatchModel>(_patch->parent()); + + PatchWindow* const preferred = ( (parent && (event->state & GDK_SHIFT_MASK)) + ? NULL + : App::instance().window_factory()->patch_window(parent) ); + + App::instance().window_factory()->present_patch(_patch, preferred); +} + + + +/** Browse to this patch in current (parent's) window + * (unless an existing window is displaying it) + */ +void +SubpatchModule::browse_to_patch() +{ + assert(_patch->parent()); + + SharedPtr<PatchModel> parent = PtrCast<PatchModel>(_patch->parent()); + + PatchWindow* const preferred = ( (parent) + ? App::instance().window_factory()->patch_window(parent) + : NULL ); + + App::instance().window_factory()->present_patch(_patch, preferred); +} + + + +void +SubpatchModule::show_dialog() +{ + cerr << "FIXME: dialog\n"; + //m_patch->show_control_window(); +} + + +void +SubpatchModule::menu_remove() +{ + App::instance().engine()->destroy(_patch->path()); +} + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/SubpatchModule.h b/src/libs/gui/SubpatchModule.h new file mode 100644 index 00000000..4dc17e8b --- /dev/null +++ b/src/libs/gui/SubpatchModule.h @@ -0,0 +1,71 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +#ifndef SUBPATCHMODULE_H +#define SUBPATCHMODULE_H + +#include <string> +#include <libgnomecanvasmm.h> +#include <raul/SharedPtr.h> +#include "client/PatchModel.h" +#include "PatchPortModule.h" +#include "NodeModule.h" +using std::string; using std::list; + +namespace Ingen { namespace Client { + class PatchModel; + class NodeModel; + class PortModel; + class PatchWindow; +} } +using namespace Ingen::Client; + +namespace Ingen { +namespace GUI { + +class PatchCanvas; +class NodeControlWindow; + + +/** A module to represent a subpatch + * + * \ingroup GUI + */ +class SubpatchModule : public NodeModule +{ +public: + SubpatchModule(boost::shared_ptr<PatchCanvas> canvas, SharedPtr<PatchModel> controller); + virtual ~SubpatchModule() {} + + void on_double_click(GdkEventButton* ev); + + void show_dialog(); + void browse_to_patch(); + void menu_remove(); + + SharedPtr<PatchModel> patch() { return _patch; } + +protected: + SharedPtr<PatchModel> _patch; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // SUBPATCHMODULE_H diff --git a/src/libs/gui/ThreadedLoader.cpp b/src/libs/gui/ThreadedLoader.cpp new file mode 100644 index 00000000..120d9b6c --- /dev/null +++ b/src/libs/gui/ThreadedLoader.cpp @@ -0,0 +1,141 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <fstream> +#include <cassert> +#include <string> +#include "client/PatchModel.h" +#include "module/Module.h" +#include "App.h" +#include "ThreadedLoader.h" +using std::cout; using std::endl; + +namespace Ingen { +namespace GUI { + + +ThreadedLoader::ThreadedLoader(SharedPtr<EngineInterface> engine) + : _serialisation_module(Ingen::Shared::load_module("ingen_serialisation")) + , _engine(engine) + , _deprecated_loader(engine) + , _serializer(*App::instance().rdf_world()) +{ + // FIXME: rework this so the thread is only present when it's doing something (save mem) + if (_serialisation_module) { + Loader* (*new_loader)() = NULL; + bool found = _serialisation_module->get_symbol("new_loader", (void*&)new_loader); + if (found) + _loader = SharedPtr<Loader>(new_loader()); + } + + if (_loader) { + start(); + } else { + cerr << "WARNING: Failed to load ingen_serialisation module, unable to load patches." << endl;; + cerr << "If you are running from the source tree, run ingenuity_dev." << endl; + } +} + + +ThreadedLoader::~ThreadedLoader() +{ +} + + +void +ThreadedLoader::_whipped() +{ + _mutex.lock(); + + while ( ! _events.empty() ) { + _events.front()(); + _events.pop_front(); + } + + _mutex.unlock(); +} + +/** FIXME: use poly parameter */ +void +ThreadedLoader::load_patch(bool merge, + const string& data_base_uri, + const Path& data_path, + MetadataMap engine_data, + optional<Path> engine_parent, + optional<const string&> engine_name, + optional<size_t> engine_poly) +{ + _mutex.lock(); + + // FIXME: Filthy hack to load deprecated patches based on file extension + if (data_base_uri.substr(data_base_uri.length()-3) == ".om") { + _events.push_back(sigc::hide_return(sigc::bind( + sigc::mem_fun(_deprecated_loader, &DeprecatedLoader::load_patch), + data_base_uri, + engine_parent, + (engine_name) ? engine_name.get() : "", + (engine_poly) ? engine_poly.get() : 1, + engine_data, + false))); + } else { + _events.push_back(sigc::hide_return(sigc::bind( + sigc::mem_fun(_loader.get(), &Ingen::Serialisation::Loader::load), + App::instance().engine(), + App::instance().rdf_world(), + data_base_uri, + engine_parent, + (engine_name) ? engine_name.get() : "", + // FIXME: poly here + "", + engine_data ))); + } + + _mutex.unlock(); + + whip(); +} + + +void +ThreadedLoader::save_patch(SharedPtr<PatchModel> model, const string& filename, bool recursive) +{ + _mutex.lock(); + + _events.push_back(sigc::hide_return(sigc::bind( + sigc::mem_fun(this, &ThreadedLoader::save_patch_event), + model, filename, recursive))); + + _mutex.unlock(); + + whip(); +} + + +void +ThreadedLoader::save_patch_event(SharedPtr<PatchModel> model, const string& filename, bool recursive) +{ + if (recursive) + cerr << "FIXME: Recursive save." << endl; + + _serializer.start_to_filename(filename); + _serializer.serialize(model); + _serializer.finish(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/ThreadedLoader.h b/src/libs/gui/ThreadedLoader.h new file mode 100644 index 00000000..58a18fae --- /dev/null +++ b/src/libs/gui/ThreadedLoader.h @@ -0,0 +1,99 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef THREADEDLOADER_H +#define THREADEDLOADER_H + +#include <string> +#include <list> +#include <cassert> +#include <boost/optional/optional.hpp> +#include <raul/Thread.h> +#include <raul/Slave.h> +#include <raul/Mutex.h> +#include <raul/Condition.h> +#include "interface/EngineInterface.h" +#include "client/PatchModel.h" +#include "client/Serializer.h" +#include "client/DeprecatedLoader.h" +#include "serialisation/Loader.h" +using std::string; +using std::list; +using boost::optional; + +using namespace Ingen::Client; +using namespace Ingen::Serialisation; + +namespace Ingen { +namespace GUI { + + +/** Thread for loading patch files. + * + * This is a seperate thread so it can send all the loading message without + * blocking everything else, so the app can respond to the incoming events + * caused as a result of the patch loading, while the patch loads. + * + * Implemented as a slave with a list of closures (events) which processes + * all events in the (mutex protected) list each time it's whipped. + * + * \ingroup GUI + */ +class ThreadedLoader : public Raul::Slave +{ +public: + ThreadedLoader(SharedPtr<EngineInterface> engine); + ~ThreadedLoader(); + + // FIXME: there's a pattern here.... + // (same core interface as Loader/Serializer) + + void load_patch(bool merge, + const string& data_base_uri, + const Path& data_path, + MetadataMap engine_data, + optional<Path> engine_parent, + optional<const string&> engine_name = optional<const string&>(), + optional<size_t> engine_poly = optional<size_t>()); + + void save_patch(SharedPtr<PatchModel> model, const string& filename, bool recursive); + +private: + + void save_patch_event(SharedPtr<PatchModel> model, const string& filename, bool recursive); + + /** Returns nothing and takes no parameters (because they have all been bound) */ + typedef sigc::slot<void> Closure; + + void _whipped(); + + SharedPtr<Glib::Module> _serialisation_module; + + SharedPtr<EngineInterface> _engine; + SharedPtr<Loader> _loader; + + DeprecatedLoader _deprecated_loader; + Serializer _serializer; + Raul::Mutex _mutex; + list<Closure> _events; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // LOADERRTHREAD_H diff --git a/src/libs/gui/UploadPatchWindow.cpp b/src/libs/gui/UploadPatchWindow.cpp new file mode 100644 index 00000000..7def01a0 --- /dev/null +++ b/src/libs/gui/UploadPatchWindow.cpp @@ -0,0 +1,283 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <sstream> +#include <algorithm> +#include <sys/types.h> +#include <dirent.h> +#include <boost/optional/optional.hpp> +#include <curl/curl.h> +#include <raul/RDFQuery.h> +#include "interface/EngineInterface.h" +#include "client/Serializer.h" +#include "client/PatchModel.h" +#include "UploadPatchWindow.h" +#include "App.h" +#include "Configuration.h" +#include "ThreadedLoader.h" + +using boost::optional; +using namespace Raul; +using namespace std; + +namespace Ingen { +namespace GUI { + + +UploadPatchWindow::UploadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Gtk::Dialog(cobject) + , _thread(NULL) + , _progress_pct(0) + , _response(0) +{ + xml->get_widget("upload_patch_symbol_entry", _symbol_entry); + xml->get_widget("upload_patch_short_name_entry", _short_name_entry); + xml->get_widget("upload_patch_progress", _upload_progress); + xml->get_widget("upload_patch_cancel_button", _cancel_button); + xml->get_widget("upload_patch_upload_button", _upload_button); + + + _symbol_entry->signal_changed().connect(sigc::mem_fun(this, &UploadPatchWindow::symbol_changed)); + _short_name_entry->signal_changed().connect(sigc::mem_fun(this, &UploadPatchWindow::short_name_changed)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &UploadPatchWindow::cancel_clicked)); + _upload_button->signal_clicked().connect(sigc::mem_fun(this, &UploadPatchWindow::upload_clicked)); +} + + +void +UploadPatchWindow::present(SharedPtr<PatchModel> patch) +{ + _patch = patch; + + Gtk::Window::present(); +} + + +void +UploadPatchWindow::on_show() +{ + Gtk::Dialog::on_show(); + + Raul::Atom atom = _patch->get_metadata("lv2:symbol"); + if (atom) + _symbol_entry->set_text(atom.get_string()); + + atom = _patch->get_metadata("doap:name"); + if (atom) + _short_name_entry->set_text(atom.get_string()); +} + + +void +UploadPatchWindow::on_hide() +{ + Gtk::Dialog::on_hide(); + + delete _thread; + _thread = NULL; +} + + +bool +UploadPatchWindow::is_symbol(const Glib::ustring& s) +{ + if (s.length() == 0) + return false; + + for (unsigned i=0; i < s.length(); ++i) + if ( !( (s[i] >= 'a' && s[i] <= 'z') + || (s[i] >= 'A' && s[i] <= 'Z') + || (s[i] == '_') + || (i > 0 && s[i] >= '0' && s[i] <= '9') ) ) + return false; + + return true; +} + + +void +UploadPatchWindow::symbol_changed() +{ + _upload_button->property_sensitive() = ( + is_symbol(_symbol_entry->get_text()) + && _short_name_entry->get_text().length() > 0); +} + + +void +UploadPatchWindow::short_name_changed() +{ + _upload_button->property_sensitive() = ( + is_symbol(_symbol_entry->get_text()) + && _short_name_entry->get_text().length() > 0); +} + + +size_t +UploadThread::curl_read_cb(void *ptr, size_t size, size_t nmemb, void *data) +{ + assert(size == 1); + + istringstream* ss = (istringstream*)data; + + return ss->readsome((char*)ptr, nmemb); +} + + +int +UploadThread::curl_progress_cb(void *thread, + double dltotal, + double dlnow, + double ultotal, + double ulnow) +{ + UploadThread* me = (UploadThread*)thread; + me->_win->set_progress(min( + (int)(min(ulnow, (double)me->_length) / me->_length * 100.0), + 99)); + return 0; +} + + +UploadThread::UploadThread(UploadPatchWindow* win, const string& str, const string& url) + : Thread("Upload") + , _curl(NULL) + , _headers(NULL) + , _win(win) + , _length(str.length()) + , _stream(str) + , _url(url) +{ + _curl = curl_easy_init(); + _headers = curl_slist_append(NULL, "Content-type: application/x-turtle"); + + curl_easy_setopt(_curl, CURLOPT_URL, url.c_str()); + curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _headers); + curl_easy_setopt(_curl, CURLOPT_UPLOAD, 1); + curl_easy_setopt(_curl, CURLOPT_READDATA, &_stream); + curl_easy_setopt(_curl, CURLOPT_READFUNCTION, &UploadThread::curl_read_cb); + curl_easy_setopt(_curl, CURLOPT_INFILESIZE, sizeof(char) * str.length()); + curl_easy_setopt(_curl, CURLOPT_NOPROGRESS, FALSE); + curl_easy_setopt(_curl, CURLOPT_PROGRESSFUNCTION, &UploadThread::curl_progress_cb); + curl_easy_setopt(_curl, CURLOPT_PROGRESSDATA, this); +} + + +void +UploadThread::_run() +{ + curl_easy_perform(_curl); + + long response; + curl_easy_getinfo(_curl, CURLINFO_RESPONSE_CODE, &response); + + printf("Server returned %ld\n", response); + + _win->set_response(response); + _win->set_progress(100); + + curl_slist_free_all(_headers); + curl_easy_cleanup(_curl); + + _headers = NULL; + _curl = NULL; +} + + +bool +UploadPatchWindow::progress_callback() +{ + const int progress = _progress_pct.get(); + const int response = _response.get(); + + _upload_progress->set_fraction(progress / 100.0); + + if (progress == 100) { + if (response == 200) { + _upload_progress->set_text("Transfer completed"); + } else { + _upload_progress->set_fraction(0.0); + char status[4]; + snprintf(status, 4, "%d", (unsigned)response); + string msg = "Transfer failed: Server returned "; + msg.append(status); + _upload_progress->set_text(msg); + } + delete _thread; + _thread = NULL; + _upload_button->set_sensitive(true); + return false; + } else { + return true; + } +} + + +void +UploadPatchWindow::upload_clicked() +{ + assert(!_thread); + + Glib::ustring symbol = _symbol_entry->get_text(); + Glib::ustring short_name = _short_name_entry->get_text(); + + _patch->set_metadata("lv2:symbol", Atom(symbol)); + App::instance().engine()->set_metadata(_patch->path(), "lv2:symbol", Atom(symbol)); + + _patch->set_metadata("doap:name", Atom(short_name)); + App::instance().engine()->set_metadata(_patch->path(), "doap:name", Atom(short_name)); + + _response = 0; + _progress_pct = 0; + + _upload_progress->set_fraction(0.0); + _upload_progress->set_text(""); + + Serializer s(*App::instance().rdf_world()); + s.start_to_string(); + s.serialize(_patch); + const string str = s.finish(); + istringstream stream(str); + + string url = "http://rdf.drobilla.net/ingen_patches/"; + url += symbol + ".ingen.ttl"; + + _thread = new UploadThread(this, str, url); + + _thread->start(); + + _upload_button->set_sensitive(false); + + Glib::signal_timeout().connect( + sigc::mem_fun(this, &UploadPatchWindow::progress_callback), 100); +} + + +void +UploadPatchWindow::cancel_clicked() +{ + if (_thread) { + delete _thread; + _thread = NULL; + } + + hide(); +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/UploadPatchWindow.h b/src/libs/gui/UploadPatchWindow.h new file mode 100644 index 00000000..13272215 --- /dev/null +++ b/src/libs/gui/UploadPatchWindow.h @@ -0,0 +1,105 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef UPLOADPATCHWINDOW_H +#define UPLOADPATCHWINDOW_H + +#include <sstream> +#include <libglademm/xml.h> +#include <gtkmm.h> +#include <curl/curl.h> +#include <raul/SharedPtr.h> +#include <raul/Thread.h> +#include <raul/AtomicInt.h> +#include "client/PatchModel.h" +#include "client/PluginModel.h" +using Ingen::Client::PatchModel; +using Ingen::Client::MetadataMap; + +namespace Ingen { +namespace GUI { + +class UploadPatchWindow; + + +class UploadThread : public Raul::Thread { +public: + UploadThread(UploadPatchWindow* win, + const string& str, + const string& url); + +private: + static size_t curl_read_cb(void* ptr, size_t size, size_t nmemb, void *stream); + static int curl_progress_cb(void* thread, double dltotal, double dlnow, double ultotal, double ulnow); + + void _run(); + + CURL* _curl; + curl_slist* _headers; + UploadPatchWindow* _win; + size_t _length; + std::istringstream _stream; + std::string _url; +}; + + +/* Upload patch dialog. + * + * \ingroup GUI + */ +class UploadPatchWindow : public Gtk::Dialog +{ +public: + UploadPatchWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml); + + void present(SharedPtr<PatchModel> patch); + + Gtk::ProgressBar* progress_bar() { return _upload_progress; } + + void set_response(int response) { _response = response; } + void set_progress(int pct) { _progress_pct = pct; } + +private: + bool is_symbol(const Glib::ustring& str); + void symbol_changed(); + void short_name_changed(); + void cancel_clicked(); + void upload_clicked(); + void on_show(); + void on_hide(); + bool progress_callback(); + + UploadThread* _thread; + + SharedPtr<PatchModel> _patch; + + Raul::AtomicInt _progress_pct; + Raul::AtomicInt _response; + + Gtk::Entry* _symbol_entry; + Gtk::Entry* _short_name_entry; + Gtk::ProgressBar* _upload_progress; + Gtk::Button* _cancel_button; + Gtk::Button* _upload_button; + +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // UPLOADPATCHWINDOW_H diff --git a/src/libs/gui/WindowFactory.cpp b/src/libs/gui/WindowFactory.cpp new file mode 100644 index 00000000..7c2e3eaa --- /dev/null +++ b/src/libs/gui/WindowFactory.cpp @@ -0,0 +1,357 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "WindowFactory.h" +#include "App.h" +#include "PatchWindow.h" +#include "GladeFactory.h" +#include "NodePropertiesWindow.h" +#include "PatchPropertiesWindow.h" +#include "NodeControlWindow.h" +#include "LoadPluginWindow.h" +#include "LoadPatchWindow.h" +#include "LoadRemotePatchWindow.h" +#include "UploadPatchWindow.h" +#include "LoadSubpatchWindow.h" +#include "RenameWindow.h" +#include "NewSubpatchWindow.h" + +namespace Ingen { +namespace GUI { + + +WindowFactory::WindowFactory() +: _load_plugin_win(NULL) +, _load_patch_win(NULL) +, _load_remote_patch_win(NULL) +, _upload_patch_win(NULL) +, _new_subpatch_win(NULL) +, _load_subpatch_win(NULL) +, _node_properties_win(NULL) +, _patch_properties_win(NULL) +{ + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference(); + + xml->get_widget_derived("load_plugin_win", _load_plugin_win); + xml->get_widget_derived("load_patch_win", _load_patch_win); + xml->get_widget_derived("load_remote_patch_win", _load_remote_patch_win); + xml->get_widget_derived("upload_patch_win", _upload_patch_win); + xml->get_widget_derived("new_subpatch_win", _new_subpatch_win); + xml->get_widget_derived("load_subpatch_win", _load_subpatch_win); + xml->get_widget_derived("node_properties_win", _node_properties_win); + xml->get_widget_derived("patch_properties_win", _patch_properties_win); +} + + +WindowFactory::~WindowFactory() +{ + for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i) + delete i->second; + + for (ControlWindowMap::iterator i = _control_windows.begin(); i != _control_windows.end(); ++i) + delete i->second; + +} + + +void +WindowFactory::clear() +{ + for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i) + delete i->second; + + _patch_windows.clear(); + + for (ControlWindowMap::iterator i = _control_windows.begin(); i != _control_windows.end(); ++i) + delete i->second; + + _control_windows.clear(); +} + + +/** Returns the number of Patch windows currently visible. + */ +size_t +WindowFactory::num_open_patch_windows() +{ + size_t ret = 0; + for (PatchWindowMap::iterator i = _patch_windows.begin(); i != _patch_windows.end(); ++i) + if (i->second->is_visible()) + ++ret; + + return ret; +} + + + +PatchWindow* +WindowFactory::patch_window(SharedPtr<PatchModel> patch) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + return (w == _patch_windows.end()) ? NULL : w->second; +} + + +NodeControlWindow* +WindowFactory::control_window(SharedPtr<NodeModel> node) +{ + ControlWindowMap::iterator w = _control_windows.find(node->path()); + + return (w == _control_windows.end()) ? NULL : w->second; +} + + +/** Present a PatchWindow for a Patch. + * + * If @a preferred is not NULL, it will be set to display @a patch if the patch + * does not already have a visible window, otherwise that window will be presented and + * @a preferred left unmodified. + */ +void +WindowFactory::present_patch(SharedPtr<PatchModel> patch, PatchWindow* preferred, SharedPtr<PatchView> view) +{ + assert( !view || view->patch() == patch); + + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) { + (*w).second->present(); + } else if (preferred) { + w = _patch_windows.find(preferred->patch()->path()); + assert((*w).second == preferred); + + preferred->set_patch(patch, view); + _patch_windows.erase(w); + _patch_windows[patch->path()] = preferred; + preferred->present(); + + } else { + PatchWindow* win = new_patch_window(patch, view); + win->present(); + } +} + + +PatchWindow* +WindowFactory::new_patch_window(SharedPtr<PatchModel> patch, SharedPtr<PatchView> view) +{ + assert( !view || view->patch() == patch); + + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeFactory::new_glade_reference("patch_win"); + + PatchWindow* win = NULL; + xml->get_widget_derived("patch_win", win); + assert(win); + + win->set_patch(patch, view); + _patch_windows[patch->path()] = win; + + win->signal_delete_event().connect(sigc::bind<0>( + sigc::mem_fun(this, &WindowFactory::remove_patch_window), win)); + + return win; +} + + +bool +WindowFactory::remove_patch_window(PatchWindow* win, GdkEventAny* ignored) +{ + if (_patch_windows.size() <= 1) { + Gtk::MessageDialog d(*win, "This is the last remaining open patch " + "window. Closing this window will exit Ingenuity (the engine will " + "remain running).\n\nAre you sure you want to quit?", + true, Gtk::MESSAGE_WARNING, Gtk::BUTTONS_NONE, true); + d.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL); + d.add_button(Gtk::Stock::QUIT, Gtk::RESPONSE_CLOSE); + int ret = d.run(); + if (ret == Gtk::RESPONSE_CLOSE) + App::instance().quit(); + else + return true; + } + + PatchWindowMap::iterator w = _patch_windows.find(win->patch()->path()); + + assert((*w).second == win); + _patch_windows.erase(w); + + delete win; + + return false; +} + + +void +WindowFactory::present_controls(SharedPtr<NodeModel> node) +{ + NodeControlWindow* win = control_window(node); + + if (win) { + win->present(); + } else { + win = new_control_window(node); + win->present(); + } +} + + +NodeControlWindow* +WindowFactory::new_control_window(SharedPtr<NodeModel> node) +{ + size_t poly = 1; + if (node->polyphonic()) + poly = ((PatchModel*)node->parent().get())->poly(); + + NodeControlWindow* win = new NodeControlWindow(node, poly); + + _control_windows[node->path()] = win; + + win->signal_delete_event().connect(sigc::bind<0>( + sigc::mem_fun(this, &WindowFactory::remove_control_window), win)); + + return win; +} + + +bool +WindowFactory::remove_control_window(NodeControlWindow* win, GdkEventAny* ignored) +{ + ControlWindowMap::iterator w = _control_windows.find(win->node()->path()); + + assert((*w).second == win); + _control_windows.erase(w); + + delete win; + + return true; +} + + +void +WindowFactory::present_load_plugin(SharedPtr<PatchModel> patch, MetadataMap data) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _load_plugin_win->set_transient_for(*w->second); + + _load_plugin_win->present(patch, data); +} + + +void +WindowFactory::present_load_patch(SharedPtr<PatchModel> patch, MetadataMap data) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _load_patch_win->set_transient_for(*w->second); + + _load_patch_win->set_merge(); // Import is the only choice + + _load_patch_win->present(patch, data); +} + + +void +WindowFactory::present_load_remote_patch(SharedPtr<PatchModel> patch, MetadataMap data) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _load_remote_patch_win->set_transient_for(*w->second); + + _load_remote_patch_win->set_merge(); // Import is the only choice + + _load_remote_patch_win->present(patch, data); +} + + +void +WindowFactory::present_upload_patch(SharedPtr<PatchModel> patch) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _upload_patch_win->set_transient_for(*w->second); + + _upload_patch_win->present(patch); +} + + +void +WindowFactory::present_new_subpatch(SharedPtr<PatchModel> patch, MetadataMap data) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _new_subpatch_win->set_transient_for(*w->second); + + _new_subpatch_win->present(patch, data); +} + + +void +WindowFactory::present_load_subpatch(SharedPtr<PatchModel> patch, MetadataMap data) +{ + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + + if (w != _patch_windows.end()) + _load_subpatch_win->set_transient_for(*w->second); + + _load_subpatch_win->present(patch, data); +} + + +void +WindowFactory::present_rename(SharedPtr<ObjectModel> object) +{ + PatchWindowMap::iterator w = _patch_windows.find(object->path()); + + if (w != _patch_windows.end()) + _rename_win->set_transient_for(*w->second); + + _rename_win->present(object); +} + + +void +WindowFactory::present_properties(SharedPtr<NodeModel> node) +{ + SharedPtr<PatchModel> patch = PtrCast<PatchModel>(node); + if (patch) { + + PatchWindowMap::iterator w = _patch_windows.find(patch->path()); + if (w != _patch_windows.end()) + _patch_properties_win->set_transient_for(*w->second); + + _patch_properties_win->present(patch); + + } else { + + PatchWindowMap::iterator w = _patch_windows.find(node->parent()->path()); + if (w != _patch_windows.end()) + _node_properties_win->set_transient_for(*w->second); + + _node_properties_win->present(node); + } +} + + +} // namespace GUI +} // namespace Ingen diff --git a/src/libs/gui/WindowFactory.h b/src/libs/gui/WindowFactory.h new file mode 100644 index 00000000..c3170dce --- /dev/null +++ b/src/libs/gui/WindowFactory.h @@ -0,0 +1,105 @@ +/* This file is part of Ingen. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) any later + * version. + * + * Ingen is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef WINDOW_FACTORY_H +#define WINDOW_FACTORY_H + +#include <map> +#include <gtkmm.h> +#include <raul/SharedPtr.h> +#include "client/PatchModel.h" +#include "PatchView.h" + +using Ingen::Client::PatchModel; + +namespace Ingen { +namespace GUI { + +class PatchWindow; +class NodeControlWindow; +class NodePropertiesWindow; +class PatchPropertiesWindow; +class LoadPatchWindow; +class LoadRemotePatchWindow; +class UploadPatchWindow; +class RenameWindow; + + +/** Manager/Factory for all windows. + * + * This serves as a nice centralized spot for all window management issues, + * as well as an enumeration of all windows (the goal being to reduce that + * number as much as possible). + */ +class WindowFactory { +public: + WindowFactory(); + ~WindowFactory(); + + size_t num_open_patch_windows(); + + PatchWindow* patch_window(SharedPtr<PatchModel> patch); + NodeControlWindow* control_window(SharedPtr<NodeModel> node); + + void present_patch(SharedPtr<PatchModel> patch, + PatchWindow* preferred = NULL, + SharedPtr<PatchView> patch = SharedPtr<PatchView>()); + + void present_controls(SharedPtr<NodeModel> node); + + void present_load_plugin(SharedPtr<PatchModel> patch, MetadataMap data = MetadataMap()); + void present_load_patch(SharedPtr<PatchModel> patch, MetadataMap data = MetadataMap()); + void present_load_remote_patch(SharedPtr<PatchModel> patch, MetadataMap data = MetadataMap()); + void present_upload_patch(SharedPtr<PatchModel> patch); + void present_new_subpatch(SharedPtr<PatchModel> patch, MetadataMap data = MetadataMap()); + void present_load_subpatch(SharedPtr<PatchModel> patch, MetadataMap data = MetadataMap()); + void present_rename(SharedPtr<ObjectModel> object); + void present_properties(SharedPtr<NodeModel> node); + + bool remove_patch_window(PatchWindow* win, GdkEventAny* ignored = NULL); + + void clear(); + +private: + typedef std::map<Path, PatchWindow*> PatchWindowMap; + typedef std::map<Path, NodeControlWindow*> ControlWindowMap; + + PatchWindow* new_patch_window(SharedPtr<PatchModel> patch, SharedPtr<PatchView> view); + + + NodeControlWindow* new_control_window(SharedPtr<NodeModel> node); + bool remove_control_window(NodeControlWindow* win, GdkEventAny* ignored); + + PatchWindowMap _patch_windows; + ControlWindowMap _control_windows; + + LoadPluginWindow* _load_plugin_win; + LoadPatchWindow* _load_patch_win; + LoadRemotePatchWindow* _load_remote_patch_win; + UploadPatchWindow* _upload_patch_win; + NewSubpatchWindow* _new_subpatch_win; + LoadSubpatchWindow* _load_subpatch_win; + NodePropertiesWindow* _node_properties_win; + PatchPropertiesWindow* _patch_properties_win; + RenameWindow* _rename_win; +}; + + +} // namespace GUI +} // namespace Ingen + +#endif // WINDOW_FACTORY_H diff --git a/src/libs/gui/cmdline.h b/src/libs/gui/cmdline.h new file mode 100644 index 00000000..6f1f9259 --- /dev/null +++ b/src/libs/gui/cmdline.h @@ -0,0 +1,86 @@ +/* cmdline.h */ + +/* File autogenerated by gengetopt version 2.19.1 */ + +#ifndef CMDLINE_H +#define CMDLINE_H + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef CMDLINE_PARSER_PACKAGE +#define CMDLINE_PARSER_PACKAGE "ingen" +#endif + +#ifndef CMDLINE_PARSER_VERSION +#define CMDLINE_PARSER_VERSION VERSION +#endif + +struct gengetopt_args_info +{ + const char *help_help; /* Print help and exit help description. */ + const char *version_help; /* Print version and exit help description. */ + int engine_flag; /* Run (JACK) engine (default=off). */ + const char *engine_help; /* Run (JACK) engine help description. */ + int engine_port_arg; /* Engine OSC port (default='16180'). */ + char * engine_port_orig; /* Engine OSC port original value given at command line. */ + const char *engine_port_help; /* Engine OSC port help description. */ + char * connect_arg; /* Connect to existing engine at OSC URI (default='osc.udp://localhost:16180'). */ + char * connect_orig; /* Connect to existing engine at OSC URI original value given at command line. */ + const char *connect_help; /* Connect to existing engine at OSC URI help description. */ + int gui_flag; /* Launch the GTK graphical interface (default=on). */ + const char *gui_help; /* Launch the GTK graphical interface help description. */ + int client_port_arg; /* Client OSC port. */ + char * client_port_orig; /* Client OSC port original value given at command line. */ + const char *client_port_help; /* Client OSC port help description. */ + char * load_arg; /* Load patch. */ + char * load_orig; /* Load patch original value given at command line. */ + const char *load_help; /* Load patch help description. */ + char * path_arg; /* Target path for loaded patch. */ + char * path_orig; /* Target path for loaded patch original value given at command line. */ + const char *path_help; /* Target path for loaded patch help description. */ + + int help_given ; /* Whether help was given. */ + int version_given ; /* Whether version was given. */ + int engine_given ; /* Whether engine was given. */ + int engine_port_given ; /* Whether engine-port was given. */ + int connect_given ; /* Whether connect was given. */ + int gui_given ; /* Whether gui was given. */ + int client_port_given ; /* Whether client-port was given. */ + int load_given ; /* Whether load was given. */ + int path_given ; /* Whether path was given. */ + +} ; + +extern const char *gengetopt_args_info_purpose; +extern const char *gengetopt_args_info_usage; +extern const char *gengetopt_args_info_help[]; + +int cmdline_parser (int argc, char * const *argv, + struct gengetopt_args_info *args_info); +int cmdline_parser2 (int argc, char * const *argv, + struct gengetopt_args_info *args_info, + int override, int initialize, int check_required); +int cmdline_parser_file_save(const char *filename, + struct gengetopt_args_info *args_info); + +void cmdline_parser_print_help(void); +void cmdline_parser_print_version(void); + +void cmdline_parser_init (struct gengetopt_args_info *args_info); +void cmdline_parser_free (struct gengetopt_args_info *args_info); + +int cmdline_parser_required (struct gengetopt_args_info *args_info, + const char *prog_name); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* CMDLINE_H */ diff --git a/src/libs/gui/ingen-icon.svg b/src/libs/gui/ingen-icon.svg new file mode 100644 index 00000000..a15ed7e7 --- /dev/null +++ b/src/libs/gui/ingen-icon.svg @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.44.1" + width="64" + height="64" + version="1.0" + sodipodi:docbase="/home/dave/code/codesonnet/ingen/src/progs/ingenuity" + sodipodi:docname="ingen-icon.svg"> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs5" /> + <sodipodi:namedview + inkscape:window-height="575" + inkscape:window-width="853" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:zoom="5.921875" + inkscape:cx="32" + inkscape:cy="31.915567" + inkscape:window-x="569" + inkscape:window-y="167" + inkscape:current-layer="svg2" /> + <path + style="fill:black" + d="M 7.3978252,58.655244 C 3.8646112,57.233665 3.1688652,52.819494 3.1688652,31.824538 C 3.1688652,10.379073 3.8359622,6.4337347 7.7136682,4.9457196 C 9.8253032,4.135409 54.512427,4.135409 56.624063,4.9457196 C 60.501768,6.4337347 61.168865,10.379073 61.168865,31.824538 C 61.168865,53.270003 60.501768,57.215342 56.624063,58.703357 C 54.702346,59.440788 9.2397932,59.396354 7.3978252,58.655244 z M 55.234606,55.289354 C 57.058692,54.313133 57.168865,52.976602 57.168865,31.824538 C 57.168865,10.672474 57.058692,9.3359433 55.234606,8.3597221 C 52.64791,6.9753642 11.68982,6.9753642 9.1031242,8.3597221 C 7.2790962,9.3359123 7.1688652,10.672309 7.1688652,31.810032 C 7.1688652,52.102806 7.3345622,54.321667 8.9188652,55.244439 C 11.279929,56.619633 52.673952,56.659775 55.234606,55.289354 z M 37.168865,48.891926 C 37.168865,47.331323 36.446365,46.230001 35.153333,45.819608 C 34.04479,45.46777 31.607131,42.787082 29.736313,39.862522 C 25.549347,33.31724 24.555639,32.64737 23.756486,35.831449 C 23.178349,38.13493 22.684027,38.324538 17.256802,38.324538 C 10.799575,38.324538 10.168865,37.746144 10.168865,31.824538 C 10.168865,25.902932 10.799575,25.324538 17.256802,25.324538 C 22.684027,25.324538 23.178349,25.514146 23.756486,27.817627 C 24.555639,31.001706 25.549347,30.331836 29.736313,23.786554 C 31.607131,20.861994 34.04479,18.181306 35.153333,17.829468 C 36.446365,17.419075 37.168865,16.317753 37.168865,14.75715 C 37.168865,12.479647 37.514343,12.324538 42.587078,12.324538 C 50.54284,12.324538 51.168865,12.761099 51.168865,18.309082 C 51.168865,24.371452 49.795116,25.518459 43.000041,25.129613 C 38.343077,24.86312 37.628453,24.53999 37.349314,22.574538 C 36.852569,19.076888 35.536131,19.853195 31.414126,26.074538 L 27.604413,31.824538 L 31.418918,37.574538 C 35.547694,43.798272 36.853067,44.568677 37.349314,41.074538 C 37.628453,39.109086 38.343077,38.785956 43.000041,38.519463 C 49.795116,38.130617 51.168865,39.277624 51.168865,45.339994 C 51.168865,50.887977 50.54284,51.324538 42.587078,51.324538 C 37.514343,51.324538 37.168865,51.169429 37.168865,48.891926 z " + id="path1873" /> +</svg> diff --git a/src/libs/gui/ingen.svg b/src/libs/gui/ingen.svg new file mode 100644 index 00000000..a15ed7e7 --- /dev/null +++ b/src/libs/gui/ingen.svg @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://web.resource.org/cc/" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + id="svg2" + sodipodi:version="0.32" + inkscape:version="0.44.1" + width="64" + height="64" + version="1.0" + sodipodi:docbase="/home/dave/code/codesonnet/ingen/src/progs/ingenuity" + sodipodi:docname="ingen-icon.svg"> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + </cc:Work> + </rdf:RDF> + </metadata> + <defs + id="defs5" /> + <sodipodi:namedview + inkscape:window-height="575" + inkscape:window-width="853" + inkscape:pageshadow="2" + inkscape:pageopacity="0.0" + guidetolerance="10.0" + gridtolerance="10.0" + objecttolerance="10.0" + borderopacity="1.0" + bordercolor="#666666" + pagecolor="#ffffff" + id="base" + inkscape:zoom="5.921875" + inkscape:cx="32" + inkscape:cy="31.915567" + inkscape:window-x="569" + inkscape:window-y="167" + inkscape:current-layer="svg2" /> + <path + style="fill:black" + d="M 7.3978252,58.655244 C 3.8646112,57.233665 3.1688652,52.819494 3.1688652,31.824538 C 3.1688652,10.379073 3.8359622,6.4337347 7.7136682,4.9457196 C 9.8253032,4.135409 54.512427,4.135409 56.624063,4.9457196 C 60.501768,6.4337347 61.168865,10.379073 61.168865,31.824538 C 61.168865,53.270003 60.501768,57.215342 56.624063,58.703357 C 54.702346,59.440788 9.2397932,59.396354 7.3978252,58.655244 z M 55.234606,55.289354 C 57.058692,54.313133 57.168865,52.976602 57.168865,31.824538 C 57.168865,10.672474 57.058692,9.3359433 55.234606,8.3597221 C 52.64791,6.9753642 11.68982,6.9753642 9.1031242,8.3597221 C 7.2790962,9.3359123 7.1688652,10.672309 7.1688652,31.810032 C 7.1688652,52.102806 7.3345622,54.321667 8.9188652,55.244439 C 11.279929,56.619633 52.673952,56.659775 55.234606,55.289354 z M 37.168865,48.891926 C 37.168865,47.331323 36.446365,46.230001 35.153333,45.819608 C 34.04479,45.46777 31.607131,42.787082 29.736313,39.862522 C 25.549347,33.31724 24.555639,32.64737 23.756486,35.831449 C 23.178349,38.13493 22.684027,38.324538 17.256802,38.324538 C 10.799575,38.324538 10.168865,37.746144 10.168865,31.824538 C 10.168865,25.902932 10.799575,25.324538 17.256802,25.324538 C 22.684027,25.324538 23.178349,25.514146 23.756486,27.817627 C 24.555639,31.001706 25.549347,30.331836 29.736313,23.786554 C 31.607131,20.861994 34.04479,18.181306 35.153333,17.829468 C 36.446365,17.419075 37.168865,16.317753 37.168865,14.75715 C 37.168865,12.479647 37.514343,12.324538 42.587078,12.324538 C 50.54284,12.324538 51.168865,12.761099 51.168865,18.309082 C 51.168865,24.371452 49.795116,25.518459 43.000041,25.129613 C 38.343077,24.86312 37.628453,24.53999 37.349314,22.574538 C 36.852569,19.076888 35.536131,19.853195 31.414126,26.074538 L 27.604413,31.824538 L 31.418918,37.574538 C 35.547694,43.798272 36.853067,44.568677 37.349314,41.074538 C 37.628453,39.109086 38.343077,38.785956 43.000041,38.519463 C 49.795116,38.130617 51.168865,39.277624 51.168865,45.339994 C 51.168865,50.887977 50.54284,51.324538 42.587078,51.324538 C 37.514343,51.324538 37.168865,51.169429 37.168865,48.891926 z " + id="path1873" /> +</svg> diff --git a/src/libs/gui/ingen_gui.glade b/src/libs/gui/ingen_gui.glade new file mode 100644 index 00000000..6eaf300e --- /dev/null +++ b/src/libs/gui/ingen_gui.glade @@ -0,0 +1,3044 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd"> +<!--*- mode: xml -*--> +<glade-interface> + <widget class="GtkWindow" id="patch_win"> + <property name="title" translatable="yes">Ingenuity</property> + <property name="default_width">640</property> + <property name="default_height">480</property> + <property name="icon">ingen.svg</property> + <child> + <widget class="GtkVBox" id="patch_win_vbox"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuBar" id="menubar"> + <property name="visible">True</property> + <child> + <widget class="GtkMenuItem" id="patch_file_menu"> + <property name="visible">True</property> + <property name="label" translatable="yes">_File</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="patch_file_menu_menu"> + <child> + <widget class="GtkImageMenuItem" id="patch_import_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Load a patch into the current patch (merge with existing contents).</property> + <property name="label" translatable="yes">_Import...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_import_menuitem_activate"/> + <accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2123"> + <property name="visible">True</property> + <property name="stock">gtk-open</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_import_location_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Import a patch from a URI</property> + <property name="label" translatable="yes">Import _Location...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_import_location1_activate"/> + <accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2124"> + <property name="visible">True</property> + <property name="stock">gtk-open</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator9"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_save_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Save this patch</property> + <property name="label">gtk-save</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_file_save_patch_menuitem_activate"/> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_save_as_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Save this patch to a specific file name</property> + <property name="label" translatable="yes">Save _As...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_save_as_menuitem_activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2125"> + <property name="visible">True</property> + <property name="stock">gtk-save-as</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_upload_menuitem"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Upload...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_upload_menuitem_activate"/> + <accelerator key="U" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2126"> + <property name="visible">True</property> + <property name="stock">gtk-network</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator10"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_configuration_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Configure OmGtk</property> + <property name="label" translatable="yes">Confi_guration...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_configuration_menuitem_activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2127"> + <property name="visible">True</property> + <property name="stock">gtk-preferences</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator11"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_close_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Close this window (patch will not be destroyed)</property> + <property name="label">gtk-close</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_patch_file_close_menuitem_activate"/> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator6"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_quit_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Quit Ingenuity (engine may continue running)</property> + <property name="label">gtk-quit</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_patch_file_quit_nokill_menuitem_activate"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="edit2"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Edit</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="edit2_menu"> + <child> + <widget class="GtkImageMenuItem" id="patch_clear_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Remove all objects from patch</property> + <property name="label">gtk-clear</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_patch_clear_menuitem_activate"/> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator99"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_cut_menuitem"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label">gtk-cut</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_patch_cut_menuitem_activate"/> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_copy_menuitem"> + <property name="visible">True</property> + <property name="label">gtk-copy</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_patch_copy_menuitem_activate"/> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_paste_menuitem"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="label">gtk-paste</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_patch_paste_menuitem_activate"/> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_delete_menuitem"> + <property name="visible">True</property> + <property name="label">gtk-delete</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_patch_delete_menuitem_activate"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="patch_patch_menu"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Patch</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="patch_patch_menu_menu"> + <child> + <widget class="GtkImageMenuItem" id="patch_fullscreen_menuitem"> + <property name="visible">True</property> + <property name="label">gtk-fullscreen</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="patch_fullscreen_menuitem"/> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator12"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_arrange_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Automatically arrange canvas</property> + <property name="label" translatable="yes">_Arrange</property> + <property name="use_underline">True</property> + <accelerator key="G" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2128"> + <property name="visible">True</property> + <property name="stock">gtk-sort-ascending</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_view_control_window_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">View/Edit controls for this patch</property> + <property name="label" translatable="yes">_Controls...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_view_control_window_menuitem_activate"/> + <accelerator key="C" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2129"> + <property name="visible">True</property> + <property name="stock">gtk-preferences</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_properties_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">View/Edit properties for this patch</property> + <property name="label" translatable="yes">_Properties...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_properties_menuitem_activate"/> + <accelerator key="P" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2130"> + <property name="visible">True</property> + <property name="stock">gtk-properties</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_destroy_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Destoy this patch (remove it from the engine)</property> + <property name="label" translatable="yes">_Destroy</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_destroy_menuitem_activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2131"> + <property name="visible">True</property> + <property name="stock">gtk-delete</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="view1"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Windows</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_view1_activate"/> + <child> + <widget class="GtkMenu" id="view1_menu"> + <child> + <widget class="GtkImageMenuItem" id="patch_view_engine_window_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Connect to, Disconnect from, or Launch Engine</property> + <property name="label" translatable="yes">_Engine...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_view_engine_window_menuitem_activate"/> + <accelerator key="E" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2132"> + <property name="visible">True</property> + <property name="stock">gtk-execute</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_view_patch_tree_window_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">View all patches in the engine as a heirarchial list</property> + <property name="label" translatable="yes">_Patch Tree...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_view_tree_window_menuitem_activate"/> + <accelerator key="T" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2133"> + <property name="visible">True</property> + <property name="stock">gtk-index</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_view_messages_window_menuitem"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">View error messages from the engine</property> + <property name="label" translatable="yes">_Messages...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_patch_view_messages_window_menuitem_activate"/> + <accelerator key="M" modifiers="GDK_CONTROL_MASK" signal="activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2134"> + <property name="visible">True</property> + <property name="stock">gtk-info</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="help_menu"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Help</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_help_menu_activate"/> + <child> + <widget class="GtkMenu" id="help_menu_menu"> + <child> + <widget class="GtkImageMenuItem" id="right-click_the_canvas_to_add_objects1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Right-click the canvas to add objects</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_right-click_the_canvas_to_add_objects1_activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2135"> + <property name="visible">True</property> + <property name="stock">gtk-info</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkSeparatorMenuItem" id="separator13"> + <property name="visible">True</property> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="patch_help_about_menuitem"> + <property name="visible">True</property> + <property name="label">gtk-about</property> + <property name="use_underline">True</property> + <property name="use_stock">True</property> + <signal name="activate" handler="on_about1_activate"/> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="patch_win_scrolledwin"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_NEVER</property> + <child> + <widget class="GtkViewport" id="patch_win_viewport"> + <property name="visible">True</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <placeholder/> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkWindow" id="load_plugin_win"> + <property name="border_width">8</property> + <property name="title" translatable="yes">Load Plugin</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="default_width">640</property> + <property name="default_height">480</property> + <property name="icon">ingen.svg</property> + <child> + <widget class="GtkVBox" id="vbox9"> + <property name="visible">True</property> + <property name="spacing">1</property> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow3"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="border_width">2</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <child> + <widget class="GtkTreeView" id="load_plugin_plugins_treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">All plugins available for loading</property> + <property name="border_width">2</property> + <property name="reorderable">True</property> + <property name="rules_hint">True</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkTable" id="table16"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">3</property> + <property name="row_spacing">12</property> + <child> + <widget class="GtkButton" id="load_plugin_clear_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Clear filter text (show all plugins)</property> + <property name="label">gtk-clear</property> + <property name="use_stock">True</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkComboBox" id="load_plugin_filter_combo"> + <property name="visible">True</property> + <property name="items" translatable="yes">Name contains: </property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="load_plugin_search_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_focus">True</property> + <property name="tooltip" translatable="yes">Search string to filter plugin list</property> + <property name="invisible_char">*</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="x_padding">6</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="load_plugin_add_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Add selected plugin to patch</property> + <property name="label">gtk-add</property> + <property name="use_stock">True</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox63"> + <property name="visible">True</property> + <child> + <widget class="GtkEntry" id="load_plugin_name_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Name of new Module</property> + <property name="invisible_char">*</property> + </widget> + </child> + <child> + <widget class="GtkCheckButton" id="load_plugin_polyphonic_checkbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Polyphonic</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="padding">8</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + <property name="x_padding">6</property> + </packing> + </child> + <child> + <widget class="GtkHSeparator" id="hseparator3"> + <property name="visible">True</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkHSeparator" id="hseparator2"> + <property name="visible">True</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkHSeparator" id="hseparator1"> + <property name="visible">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label66"> + <property name="visible">True</property> + <property name="xalign">1</property> + <property name="label" translatable="yes">Node Name:</property> + <property name="use_markup">True</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkWindow" id="new_subpatch_win"> + <property name="width_request">320</property> + <property name="border_width">8</property> + <property name="title" translatable="yes">Create Subpatch</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="icon">ingen.svg</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <child> + <widget class="GtkVBox" id="vbox4"> + <property name="visible">True</property> + <child> + <widget class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <child> + <widget class="GtkEntry" id="new_subpatch_name_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_focus">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="y_options"></property> + <property name="y_padding">4</property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="new_subpatch_polyphony_spinbutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="adjustment">1 0 100 1 10 10</property> + <property name="climb_rate">1</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + <property name="y_padding">4</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label9"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Polyphony: </property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_EXPAND</property> + <property name="x_padding">5</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label8"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Name: </property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_EXPAND</property> + <property name="x_padding">5</property> + </packing> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="new_subpatch_message_label"> + <property name="visible">True</property> + <property name="wrap">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkHButtonBox" id="hbuttonbox5"> + <property name="visible">True</property> + <property name="spacing">4</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="new_subpatch_cancel_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="new_subpatch_ok_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <child> + <widget class="GtkAlignment" id="alignment2"> + <property name="visible">True</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <child> + <widget class="GtkHBox" id="hbox54"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkImage" id="image259"> + <property name="visible">True</property> + <property name="stock">gtk-ok</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label96"> + <property name="visible">True</property> + <property name="label">Create</property> + <property name="use_underline">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkFileChooserDialog" id="load_subpatch_win"> + <property name="title" translatable="yes">Load Subpatch</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="icon">ingen.svg</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox1"> + <property name="visible">True</property> + <property name="spacing">24</property> + <child> + <widget class="GtkTable" id="table6"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">4</property> + <property name="column_spacing">12</property> + <property name="row_spacing">4</property> + <child> + <widget class="GtkHBox" id="hbox45"> + <property name="visible">True</property> + <child> + <widget class="GtkRadioButton" id="load_subpatch_name_from_user_radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Specify the name for the new patch</property> + <property name="label" translatable="yes">Specify: </property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <property name="group">load_subpatch_name_from_file_radio</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="load_subpatch_name_entry"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Specify the name for the new patch</property> + <property name="invisible_char">*</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label104"> + <property name="visible">True</property> + <property name="xalign">0</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="load_subpatch_poly_from_parent_radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Set polyphony to the same value as the parent (containing) patch</property> + <property name="label" translatable="yes">Same as parent (?)</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <property name="group">load_subpatch_poly_from_file_radio</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox46"> + <property name="visible">True</property> + <child> + <widget class="GtkRadioButton" id="load_subpatch_poly_from_user_radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property> + <property name="label" translatable="yes">Specify: </property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <property name="group">load_subpatch_poly_from_file_radio</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="load_subpatch_poly_spinbutton"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property> + <property name="adjustment">1 0 1000 1 10 10</property> + <property name="climb_rate">1</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="load_subpatch_poly_from_file_radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Use the polyphony value stored in the patch file</property> + <property name="label" translatable="yes">Load from file</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="load_subpatch_name_from_file_radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Use the name stored in the patch file</property> + <property name="label" translatable="yes">Load from file</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label80"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><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="draw_indicator">True</property> + <property name="group">load_patch_poly_from_current_radio</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="load_patch_poly_spinbutton"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Specify a custom polyphony value for new patch</property> + <property name="adjustment">1 0 100 1 10 10</property> + <property name="climb_rate">1</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">3</property> + <property name="right_attach">4</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="load_patch_poly_from_file_radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Use the polyphony value stored in the patch file</property> + <property name="label" translatable="yes">Load from file</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <property name="group">load_patch_poly_from_current_radio</property> + </widget> + <packing> + <property name="left_attach">2</property> + <property name="right_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="load_patch_poly_from_current_radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Use the same polyphony as the current patch</property> + <property name="label" translatable="yes">Keep current</property> + <property name="use_underline">True</property> + <property name="active">True</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label123"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><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">4</property> + <property name="n_columns">2</property> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <placeholder/> + </child> + <child> + <widget class="GtkVBox" id="control_strip"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <widget class="GtkHBox" id="hbox1"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <child> + <widget class="GtkLabel" id="control_strip_name_label"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="xpad">4</property> + <property name="ypad">4</property> + <property name="label" translatable="yes"><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="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="xalign">1</property> + <property name="yalign">1</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <property name="top_padding">2</property> + <property name="left_padding">2</property> + <property name="right_padding">2</property> + <child> + <widget class="GtkSpinButton" id="control_strip_spinner"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="width_chars">12</property> + <property name="adjustment">0 -9.9999999999999999e+45 1.0000000000000001e+63 1 10 10</property> + <property name="digits">4</property> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkHScale" id="control_strip_slider"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + <property name="adjustment">0 -1e+113 1e+137 0 0 0</property> + <property name="digits">63</property> + <property name="draw_value">False</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + </packing> + </child> + <child> + <widget class="GtkHSeparator" id="hseparator5"> + <property name="visible">True</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="patch_view_box"> + <property name="visible">True</property> + <child> + <widget class="GtkHBox" id="hbox70"> + <property name="visible">True</property> + <child> + <widget class="GtkToolbar" id="toolbar6"> + <property name="visible">True</property> + <property name="toolbar_style">GTK_TOOLBAR_BOTH_HORIZ</property> + <child> + <widget class="GtkToolItem" id="patch_view_breadcrumb_toolitem"> + <property name="visible">True</property> + <child> + <widget class="GtkAlignment" id="alignment4"> + <property name="visible">True</property> + <child> + <widget class="GtkViewport" id="patch_view_breadcrumb_container"> + <property name="visible">True</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <placeholder/> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + <child> + <widget class="GtkToolItem" id="toolitem14"> + <property name="visible">True</property> + <child> + <widget class="GtkLabel" id="label132"> + <property name="visible">True</property> + <property name="label" translatable="yes"> </property> + </widget> + </child> + </widget> + <packing> + <property name="homogeneous">False</property> + </packing> + </child> + </widget> + </child> + <child> + <widget class="GtkToolbar" id="toolbar3"> + <property name="visible">True</property> + <property name="show_arrow">False</property> + <child> + <widget class="GtkSeparatorToolItem" id="separatortoolitem17"> + <property name="visible">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + <child> + <widget class="GtkToggleToolButton" id="patch_view_process_but"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Enable DSP processing</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-execute</property> + <property name="active">True</property> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkToolItem" id="toolitem7"> + <property name="visible">True</property> + <child> + <widget class="GtkImage" id="image1978"> + <property name="visible">True</property> + <property name="xpad">4</property> + <property name="stock">gtk-copy</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + <child> + <widget class="GtkToolItem" id="toolitem10"> + <property name="visible">True</property> + <child> + <widget class="GtkSpinButton" id="patch_view_poly_spin"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="adjustment">1 0 100 1 10 10</property> + <property name="climb_rate">1</property> + </widget> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + <child> + <widget class="GtkSeparatorToolItem" id="separatortoolitem18"> + <property name="visible">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + <child> + <widget class="GtkToolButton" id="patch_view_save_but"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Save patch to a file</property> + <property name="stock_id">gtk-save</property> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkSeparatorToolItem" id="separatortoolitem19"> + <property name="visible">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + <child> + <widget class="GtkToolButton" id="patch_view_clear_but"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Clear (Destroy all children)</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-clear</property> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkToolButton" id="patch_view_destroy_but"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Destroy this patch</property> + <property name="stock_id">gtk-delete</property> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkSeparatorToolItem" id="separatortoolitem22"> + <property name="visible">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="homogeneous">False</property> + </packing> + </child> + <child> + <widget class="GtkToolButton" id="patch_view_refresh_but"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Refresh view</property> + <property name="stock_id">gtk-refresh</property> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkToolButton" id="patch_view_zoom_normal_but"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Zoom to normal size</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-zoom-100</property> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkToolButton" id="patch_view_zoom_full_but"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Fit patch to window</property> + <property name="use_underline">True</property> + <property name="stock_id">gtk-zoom-fit</property> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="patch_view_scrolledwindow"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="border_width">1</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <placeholder/> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="control_panel_vbox"> + <property name="visible">True</property> + <child> + <widget class="GtkAlignment" id="alignment6"> + <property name="visible">True</property> + <property name="yalign">0</property> + <child> + <widget class="GtkScrolledWindow" id="scrolledwin1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_NEVER</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <child> + <widget class="GtkViewport" id="viewport1"> + <property name="visible">True</property> + <property name="shadow_type">GTK_SHADOW_NONE</property> + <child> + <widget class="GtkVBox" id="control_panel_controls_box"> + <property name="visible">True</property> + <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property> + </widget> + </child> + </widget> + </child> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkHBox" id="control_panel_voice_controls_box"> + <property name="visible">True</property> + <property name="homogeneous">True</property> + <child> + <widget class="GtkRadioButton" id="control_panel_all_voices_radio"> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Apply changed controls to all voices</property> + <property name="label" translatable="yes">All Voices</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox32"> + <property name="visible">True</property> + <property name="spacing">5</property> + <child> + <widget class="GtkRadioButton" id="control_panel_specific_voice_radio"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Apply changed controls to one voice only</property> + <property name="label" translatable="yes">Specific Voice:</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <property name="group">control_panel_all_voices_radio</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="control_panel_voice_spinbutton"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Voice control changes are applied to</property> + <property name="adjustment">1 1 100 1 10 10</property> + <property name="climb_rate">1</property> + <property name="numeric">True</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">5</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <widget class="GtkWindow" id="messages_win"> + <property name="width_request">400</property> + <property name="height_request">180</property> + <property name="border_width">8</property> + <property name="title" translatable="yes">Messages - Ingenuity</property> + <property name="icon">ingen.svg</property> + <child> + <widget class="GtkVBox" id="vbox12"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow2"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <widget class="GtkTextView" id="messages_textview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Error messages from the engine since the last time "Clear" was pressed</property> + <property name="pixels_above_lines">5</property> + <property name="pixels_below_lines">5</property> + <property name="editable">False</property> + <property name="wrap_mode">GTK_WRAP_WORD</property> + <property name="left_margin">5</property> + <property name="right_margin">5</property> + <property name="cursor_visible">False</property> + <property name="accepts_tab">False</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkHButtonBox" id="hbuttonbox8"> + <property name="visible">True</property> + <property name="spacing">6</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="messages_clear_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-clear</property> + <property name="use_stock">True</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="messages_close_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkWindow" id="config_win"> + <property name="border_width">8</property> + <property name="title" translatable="yes">Configuration - Ingenuity</property> + <property name="icon">ingen.svg</property> + <child> + <widget class="GtkVBox" id="vbox13"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkTable" id="table9"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <child> + <widget class="GtkLabel" id="label103"> + <property name="visible">True</property> + <property name="xalign">0</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label91"> + <property name="visible">True</property> + <property name="label" translatable="yes"><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> + </widget> + </child> + <child> + <widget class="GtkButton" id="config_cancel_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="config_ok_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Apply these settings to this session only</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkWindow" id="patch_properties_win"> + <property name="width_request">400</property> + <property name="height_request">200</property> + <property name="border_width">8</property> + <property name="title" translatable="yes">Patch Description</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="icon">ingen.svg</property> + <child> + <widget class="GtkVBox" id="vbox14"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkHBox" id="hbox51"> + <property name="visible">True</property> + <property name="spacing">5</property> + <child> + <widget class="GtkLabel" id="label93"> + <property name="visible">True</property> + <property name="label" translatable="yes">Author:</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="properties_author_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">*</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow9"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <widget class="GtkTextView" id="properties_description_textview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">A short description of the patch to be included in the patch file</property> + <property name="wrap_mode">GTK_WRAP_WORD</property> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkHButtonBox" id="hbuttonbox3"> + <property name="visible">True</property> + <property name="spacing">5</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="properties_cancel_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="properties_ok_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="tooltip" translatable="yes">Apply these changes to be saved the next time the patch is saved</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">2</property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkWindow" id="rename_win"> + <property name="width_request">250</property> + <property name="title" translatable="yes">Rename</property> + <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property> + <property name="icon">ingen.svg</property> + <child> + <widget class="GtkVBox" id="vbox15"> + <property name="visible">True</property> + <property name="border_width">5</property> + <child> + <widget class="GtkHBox" id="hbox53"> + <property name="visible">True</property> + <child> + <widget class="GtkLabel" id="label95"> + <property name="visible">True</property> + <property name="label" translatable="yes">New name: </property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="rename_name_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + <child> + <widget class="GtkLabel" id="rename_message_label"> + <property name="visible">True</property> + <property name="wrap">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="padding">6</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkHButtonBox" id="hbuttonbox4"> + <property name="visible">True</property> + <property name="spacing">5</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="rename_cancel_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="rename_ok_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <child> + <widget class="GtkAlignment" id="alignment1"> + <property name="visible">True</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <child> + <widget class="GtkHBox" id="hbox52"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkImage" id="image258"> + <property name="visible">True</property> + <property name="stock">gtk-ok</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label94"> + <property name="visible">True</property> + <property name="label" translatable="yes">Rename</property> + <property name="use_underline">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">2</property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkWindow" id="node_properties_win"> + <property name="border_width">6</property> + <property name="title" translatable="yes">Node Properties - Ingenuity</property> + <property name="icon">ingen.svg</property> + <child> + <widget class="GtkVBox" id="vbox17"> + <property name="visible">True</property> + <child> + <widget class="GtkLabel" id="label105"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes"><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="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="name">Ingenuity</property> + <property name="copyright" translatable="yes">Copyright (C) 2005-2007 Dave Robillard <http://drobilla.net></property> + <property name="comments" translatable="yes">A graphical client for the Ingen audio system</property> + <property name="website">http://drobilla.net/software/ingen</property> + <property name="license" translatable="yes">Licensed under the GNU GPL, Version 2. + +See COPYING file included with this distribution, or http://www.gnu.org/licenses/gpl.txt for more information</property> + <property name="authors">Author: + Dave Robillard <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 - Ingenuity</property> + <property name="icon">ingen.svg</property> + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow8"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="border_width">3</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <widget class="GtkTreeView" id="patches_treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">All patches loaded in the engine</property> + <property name="rules_hint">True</property> + </widget> + </child> + </widget> + </child> + </widget> + <widget class="GtkDialog" id="connect_win"> + <property name="border_width">6</property> + <property name="title" translatable="yes">Engine - Ingenuity</property> + <property name="resizable">False</property> + <property name="icon">ingen.svg</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox3"> + <property name="visible">True</property> + <property name="spacing">6</property> + <child> + <widget class="GtkVBox" id="vbox19"> + <property name="visible">True</property> + <child> + <widget class="GtkHBox" id="hbox61"> + <property name="visible">True</property> + <child> + <widget class="GtkImage" id="connect_icon"> + <property name="visible">True</property> + <property name="xpad">12</property> + <property name="stock">gtk-disconnect</property> + <property name="icon_size">3</property> + </widget> + <packing> + <property name="expand">False</property> + </packing> + </child> + <child> + <widget class="GtkVBox" id="vbox20"> + <property name="visible">True</property> + <property name="border_width">5</property> + <property name="homogeneous">True</property> + <child> + <widget class="GtkProgressBar" id="connect_progress_bar"> + <property name="visible">True</property> + <property name="pulse_step">0.10000000149</property> + <property name="text" translatable="yes"></property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="connect_progress_label"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Not Connected</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkHSeparator" id="hseparator4"> + <property name="visible">True</property> + </widget> + <packing> + <property name="padding">4</property> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkTable" id="table18"> + <property name="visible">True</property> + <property name="n_rows">3</property> + <property name="n_columns">2</property> + <property name="row_spacing">8</property> + <child> + <widget class="GtkLabel" id="label131"> + <property name="visible">True</property> + <property name="xalign">0</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="connect_internal_radiobutton"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Use internal engine</property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <property name="group">connect_server_radiobutton</property> + </widget> + <packing> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="connect_launch_radiobutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Launch and connect to server on port: </property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + <property name="group">connect_server_radiobutton</property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkRadioButton" id="connect_server_radiobutton"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Connect to running server at: </property> + <property name="use_underline">True</property> + <property name="draw_indicator">True</property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox67"> + <property name="visible">True</property> + <child> + <widget class="GtkEntry" id="connect_url_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">*</property> + <property name="activates_default">True</property> + <property name="width_chars">28</property> + <property name="text" translatable="yes">osc.udp://localhost:16180</property> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options">GTK_FILL</property> + <property name="x_padding">8</property> + </packing> + </child> + <child> + <widget class="GtkHBox" id="hbox64"> + <property name="visible">True</property> + <child> + <widget class="GtkSpinButton" id="connect_port_spinbutton"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="adjustment">16180 1 65535 1 10 10</property> + <property name="climb_rate">1</property> + <property name="numeric">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options">GTK_FILL</property> + <property name="x_padding">8</property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area3"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="connect_quit_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-quit</property> + <property name="use_stock">True</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="connect_disconnect_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-disconnect</property> + <property name="use_stock">True</property> + <property name="response_id">-6</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + <child> + <widget class="GtkButton" id="connect_connect_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="label">gtk-connect</property> + <property name="use_stock">True</property> + <property name="response_id">-6</property> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkMenu" id="canvas_menu"> + <property name="visible">True</property> + <child> + <widget class="GtkImageMenuItem" id="input1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Input</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="input1_menu"> + <child> + <widget class="GtkMenuItem" id="canvas_menu_add_audio_input"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Add an audio input to this patch</property> + <property name="label" translatable="yes">Audio</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_add_audio_input_activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="canvas_menu_add_control_input"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Add a control input (and a control slider for it) to this patch</property> + <property name="label" translatable="yes">Control</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_add_control_input_activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="canvas_menu_add_midi_input"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Add a MIDI input to this patch</property> + <property name="label" translatable="yes">MIDI</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_add_midi_input_activate"/> + </widget> + </child> + </widget> + </child> + <child internal-child="image"> + <widget class="GtkImage" id="image1886"> + <property name="visible">True</property> + <property name="stock">gtk-connect</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="output1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Output</property> + <property name="use_underline">True</property> + <child> + <widget class="GtkMenu" id="output1_menu"> + <child> + <widget class="GtkMenuItem" id="canvas_menu_add_audio_output"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Add an audio output to this patch</property> + <property name="label" translatable="yes">Audio</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_add_audio_output_activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="canvas_menu_add_control_output"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Add a control output to this patch</property> + <property name="label" translatable="yes">Control</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_add_control_output_activate"/> + </widget> + </child> + <child> + <widget class="GtkMenuItem" id="canvas_menu_add_midi_output"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Add a MIDI output to this patch</property> + <property name="label" translatable="yes">MIDI</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_add_midi_output_activate"/> + </widget> + </child> + </widget> + </child> + <child internal-child="image"> + <widget class="GtkImage" id="image1887"> + <property name="visible">True</property> + <property name="stock">gtk-connect</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="canvas_menu_load_plugin"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Load a plugin as a child of this patch</property> + <property name="label" translatable="yes">_Plugin...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_add_plugin_activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image1888"> + <property name="visible">True</property> + <property name="stock">gtk-execute</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="canvas_menu_load_patch"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Load a patch as a child of this patch</property> + <property name="label" translatable="yes">_Load Patch...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_load_patch_activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image1889"> + <property name="visible">True</property> + <property name="stock">gtk-open</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkImageMenuItem" id="canvas_menu_new_patch"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Create a new (empty) patch as a child of this patch</property> + <property name="label" translatable="yes">_New Patch...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_canvas_menu_new_patch_activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image1890"> + <property name="visible">True</property> + <property name="stock">gtk-new</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + </widget> + <widget class="GtkDialog" id="load_remote_patch_win"> + <property name="border_width">8</property> + <property name="title" translatable="yes">Load Remote Patch</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox4"> + <property name="visible">True</property> + <property name="spacing">8</property> + <child> + <widget class="GtkVBox" id="vbox21"> + <property name="visible">True</property> + <property name="spacing">8</property> + <child> + <widget class="GtkScrolledWindow" id="load_remote_patch_treeview_sw"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="has_focus">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <child> + <widget class="GtkTreeView" id="load_remote_patch_treeview"> + <property name="visible">True</property> + <property name="can_focus">True</property> + </widget> + </child> + </widget> + </child> + <child> + <widget class="GtkHBox" id="hbox71"> + <property name="visible">True</property> + <child> + <widget class="GtkLabel" id="label133"> + <property name="visible">True</property> + <property name="label" translatable="yes">URI: </property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="load_remote_patch_uri_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="invisible_char">*</property> + <property name="width_chars">78</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area4"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="load_remote_patch_cancel_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="response_id">-6</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="load_remote_patch_open_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="label">gtk-open</property> + <property name="use_stock">True</property> + <property name="response_id">-5</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkDialog" id="upload_patch_win"> + <property name="border_width">8</property> + <property name="title" translatable="yes">Upload Patch</property> + <property name="resizable">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox5"> + <property name="visible">True</property> + <property name="spacing">9</property> + <child> + <widget class="GtkTable" id="table19"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="row_spacing">8</property> + <child> + <widget class="GtkLabel" id="label136"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Short Name: </property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label135"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Symbol: </property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="upload_patch_short_name_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Enter a short name for this patch, e.g. "Mega Synth"</property> + <property name="invisible_char">*</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkEntry" id="upload_patch_symbol_entry"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="tooltip" translatable="yes">Enter a short name suitable for use as an identifier or filename. + +The first character must be one of _, a-z or A-Z and subsequenct characters can be from _, a-z, A-Z or 0-9. +</property> + <property name="invisible_char">*</property> + <property name="activates_default">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label137"> + <property name="visible">True</property> + <property name="ypad">4</property> + <property name="label" translatable="yes">Succesfully uploaded patches will be available immediately in the remote patch browser. + +By uploading patches, you agree to license them under the Creative Commons Attribution-Share Alike 3.0 License. + +Thank you for contributing.</property> + <property name="wrap">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">3</property> + </packing> + </child> + <child> + <widget class="GtkProgressBar" id="upload_patch_progress"> + <property name="visible">True</property> + <property name="tooltip" translatable="yes">Upload progress</property> + <property name="pulse_step">0.10000000149</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">4</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area5"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="upload_patch_cancel_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-close</property> + <property name="use_stock">True</property> + <property name="response_id">-7</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="upload_patch_upload_button"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="response_id">-5</property> + <child> + <widget class="GtkAlignment" id="alignment5"> + <property name="visible">True</property> + <property name="xscale">0</property> + <property name="yscale">0</property> + <child> + <widget class="GtkHBox" id="hbox72"> + <property name="visible">True</property> + <property name="spacing">2</property> + <child> + <widget class="GtkImage" id="image2136"> + <property name="visible">True</property> + <property name="stock">gtk-ok</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label134"> + <property name="visible">True</property> + <property name="label" translatable="yes">Upload</property> + <property name="use_underline">True</property> + </widget> + <packing> + <property name="expand">False</property> + <property name="fill">False</property> + <property name="position">1</property> + </packing> + </child> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + </child> + </widget> + <widget class="GtkMenu" id="port_control_menu"> + <child> + <widget class="GtkImageMenuItem" id="port_control_menu_properties"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Properties...</property> + <property name="use_underline">True</property> + <signal name="activate" handler="on_port_control_menu_properties_activate"/> + <child internal-child="image"> + <widget class="GtkImage" id="image2137"> + <property name="visible">True</property> + <property name="stock">gtk-properties</property> + <property name="icon_size">1</property> + </widget> + </child> + </widget> + </child> + </widget> + <widget class="GtkDialog" id="port_properties_win"> + <property name="border_width">8</property> + <property name="type">GTK_WINDOW_POPUP</property> + <property name="title" translatable="yes">Port Properties - Ingenuity</property> + <property name="resizable">False</property> + <property name="window_position">GTK_WIN_POS_MOUSE</property> + <property name="destroy_with_parent">True</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox6"> + <property name="visible">True</property> + <property name="spacing">8</property> + <child> + <widget class="GtkTable" id="table20"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="column_spacing">2</property> + <property name="row_spacing">4</property> + <child> + <widget class="GtkLabel" id="label139"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Maximum Value: </property> + </widget> + <packing> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkLabel" id="label138"> + <property name="visible">True</property> + <property name="xalign">0</property> + <property name="label" translatable="yes">Minimum Value: </property> + </widget> + <packing> + <property name="x_options">GTK_FILL</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="port_properties_max_spinner"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="adjustment">1 -99999 99999 1 10 10</property> + <property name="climb_rate">1</property> + <property name="digits">5</property> + <property name="numeric">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + <child> + <widget class="GtkSpinButton" id="port_properties_min_spinner"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="adjustment">0 -100000000 100000000 1 10 10</property> + <property name="climb_rate">1</property> + <property name="digits">5</property> + <property name="numeric">True</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="position">2</property> + </packing> + </child> + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area6"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + <child> + <widget class="GtkButton" id="port_properties_cancel_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="response_id">-6</property> + </widget> + </child> + <child> + <widget class="GtkButton" id="port_properties_ok_button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="can_default">True</property> + <property name="has_default">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="response_id">-5</property> + </widget> + <packing> + <property name="position">1</property> + </packing> + </child> + </widget> + <packing> + <property name="expand">False</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + </widget> + </child> + </widget> +</glade-interface> diff --git a/src/libs/gui/ingen_gui.gladep b/src/libs/gui/ingen_gui.gladep new file mode 100644 index 00000000..7cd9c6ce --- /dev/null +++ b/src/libs/gui/ingen_gui.gladep @@ -0,0 +1,9 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-project SYSTEM "http://glade.gnome.org/glade-project-2.0.dtd"> + +<glade-project> + <name>Ingenuity</name> + <program_name>ingenuity</program_name> + <language>C++</language> + <gnome_support>FALSE</gnome_support> +</glade-project> |