From 14401d11e598651e7caf39cce884362e58ef5941 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 17 Aug 2008 23:00:34 +0000 Subject: Copy/paste of connections. git-svn-id: http://svn.drobilla.net/lad/ingen@1426 a436a847-0d15-0410-975c-d299462d15a1 --- src/libs/client/ClientStore.cpp | 18 ++--- src/libs/client/ClientStore.hpp | 5 +- src/libs/client/PatchModel.cpp | 25 ------- src/libs/client/PatchModel.hpp | 4 - src/libs/gui/LoadPluginWindow.cpp | 3 +- src/libs/gui/PatchCanvas.cpp | 14 ++-- src/libs/serialisation/Parser.cpp | 71 +++++++++++------- src/libs/serialisation/Parser.hpp | 9 +++ src/libs/shared/Builder.cpp | 15 ++-- src/libs/shared/ClashAvoider.cpp | 154 ++++++++++++++++++++++++++++++++++++++ src/libs/shared/ClashAvoider.hpp | 96 ++++++++++++++++++++++++ src/libs/shared/Makefile.am | 2 + src/libs/shared/Store.cpp | 23 ++++++ src/libs/shared/Store.hpp | 3 + 14 files changed, 363 insertions(+), 79 deletions(-) create mode 100644 src/libs/shared/ClashAvoider.cpp create mode 100644 src/libs/shared/ClashAvoider.hpp diff --git a/src/libs/client/ClientStore.cpp b/src/libs/client/ClientStore.cpp index 6c2c81ff..b6477b1f 100644 --- a/src/libs/client/ClientStore.cpp +++ b/src/libs/client/ClientStore.cpp @@ -117,10 +117,13 @@ ClientStore::resolve_plugin_orphans(SharedPtr plugin) void ClientStore::add_connection_orphan(std::pair orphan) { - if (!_handle_orphans) - return; - cerr << "WARNING: Orphan connection " << orphan.first - << " -> " << orphan.second << " received." << endl; + // Do this anyway, it's needed to get the connections for copy&paste + //if (!_handle_orphans) + //return; + + if (_handle_orphans) + cerr << "WARNING: Orphan connection " << orphan.first + << " -> " << orphan.second << " received." << endl; _connection_orphans.push_back(orphan); } @@ -587,9 +590,8 @@ ClientStore::attempt_connection(const Path& src_port_path, const Path& dst_port_ { SharedPtr src_port = PtrCast(object(src_port_path)); SharedPtr dst_port = PtrCast(object(dst_port_path)); - - if (src_port && dst_port) { + if (src_port && dst_port) { assert(src_port->parent()); assert(dst_port->parent()); @@ -602,13 +604,9 @@ ClientStore::attempt_connection(const Path& src_port_path, const Path& dst_port_ dst_port->connected_to(src_port); patch->add_connection(cm); - return true; - } else if (add_orphan) { - add_connection_orphan(make_pair(src_port_path, dst_port_path)); - } return false; diff --git a/src/libs/client/ClientStore.hpp b/src/libs/client/ClientStore.hpp index 4b4d9742..d904a3e3 100644 --- a/src/libs/client/ClientStore.hpp +++ b/src/libs/client/ClientStore.hpp @@ -79,6 +79,9 @@ public: void set_voice_value(const string& port_path, uint32_t voice, const Raul::Atom& value); void connect(const string& src_port_path, const string& dst_port_path); void disconnect(const string& src_port_path, const string& dst_port_path); + + typedef list< std::pair > ConnectionRecords; + const ConnectionRecords& connection_records() { return _connection_orphans; } sigc::signal > signal_new_object; sigc::signal > signal_new_plugin; @@ -138,7 +141,7 @@ private: Raul::PathTable > > _variable_orphans; /** Ditto */ - list > _connection_orphans; + ConnectionRecords _connection_orphans; }; diff --git a/src/libs/client/PatchModel.cpp b/src/libs/client/PatchModel.cpp index 69365719..af20c9f8 100644 --- a/src/libs/client/PatchModel.cpp +++ b/src/libs/client/PatchModel.cpp @@ -186,30 +186,5 @@ PatchModel::polyphonic() const } -unsigned -PatchModel::child_name_offset(ClientStore& store, - SharedPtr parent, - const string& base_name) -{ - assert(Path::is_valid_name(base_name)); - unsigned offset = 0; - - while (true) { - std::stringstream ss; - ss << base_name; - if (offset > 0) - ss << "_" << offset; - if (store.find(parent->path().base() + ss.str()) == store.end()) - break; - else if (offset == 0) - offset = 2; - else - ++offset; - } - - return offset; -} - - } // namespace Client } // namespace Ingen diff --git a/src/libs/client/PatchModel.hpp b/src/libs/client/PatchModel.hpp index b1c03cd0..70c8df0e 100644 --- a/src/libs/client/PatchModel.hpp +++ b/src/libs/client/PatchModel.hpp @@ -66,10 +66,6 @@ public: virtual void set_property(const string& key, const Atom& value); - static unsigned child_name_offset(ClientStore& store, - SharedPtr parent, - const string& base_name); - // Signals sigc::signal > signal_new_node; sigc::signal > signal_removed_node; diff --git a/src/libs/gui/LoadPluginWindow.cpp b/src/libs/gui/LoadPluginWindow.cpp index 436de188..b63de230 100644 --- a/src/libs/gui/LoadPluginWindow.cpp +++ b/src/libs/gui/LoadPluginWindow.cpp @@ -288,7 +288,8 @@ LoadPluginWindow::plugin_selection_changed() if (iter) { Gtk::TreeModel::Row row = *iter; boost::shared_ptr p = row.get_value(_plugins_columns._col_plugin_model); - _plugin_name_offset = PatchModel::child_name_offset(*App::instance().store().get(), _patch, p->default_node_name()); + _plugin_name_offset = App::instance().store()->child_name_offset( + _patch->path(), p->default_node_name()); _node_name_entry->set_text(generate_module_name(_plugin_name_offset)); } else { _plugin_name_offset = 0; diff --git a/src/libs/gui/PatchCanvas.cpp b/src/libs/gui/PatchCanvas.cpp index 040cf3c7..46cde832 100644 --- a/src/libs/gui/PatchCanvas.cpp +++ b/src/libs/gui/PatchCanvas.cpp @@ -24,6 +24,7 @@ #include #include "interface/EngineInterface.hpp" #include "shared/Builder.hpp" +#include "shared/ClashAvoider.hpp" #include "serialisation/Serialiser.hpp" #include "client/PluginModel.hpp" #include "client/PatchModel.hpp" @@ -583,15 +584,14 @@ PatchCanvas::paste() Builder builder(*App::instance().engine()); ClientStore clipboard; + ClashAvoider avoider(*App::instance().store().get(), clipboard); clipboard.new_patch("/", _patch->poly()); clipboard.set_plugins(App::instance().store()->plugins()); - parser->parse_string(App::instance().world(), &clipboard, str, "/"); + parser->parse_string(App::instance().world(), &avoider, str, "/"); + for (Store::iterator i = clipboard.begin(); i != clipboard.end(); ++i) { if (i->first == "/") continue; - /*GraphObject::Properties::iterator s = i->second->properties().find("ingen:symbol"); - const string sym = string(s->second.get_string()) + "_copy"; - s->second = sym;*/ GraphObject::Variables::iterator x = i->second->variables().find("ingenuity:canvas-x"); if (x != i->second->variables().end()) x->second = x->second.get_float() + 20.0f; @@ -605,6 +605,10 @@ PatchCanvas::paste() i->second->properties().insert(make_pair("ingen:selected", true)); builder.build(i->second); } + + for (ClientStore::ConnectionRecords::const_iterator i = clipboard.connection_records().begin(); + i != clipboard.connection_records().end(); ++i) + App::instance().engine()->connect(i->first, i->second); } @@ -659,7 +663,7 @@ void PatchCanvas::load_plugin(SharedPtr plugin) { string name = plugin->default_node_name(); - unsigned offset = PatchModel::child_name_offset(*App::instance().store().get(), _patch, name); + unsigned offset = App::instance().store()->child_name_offset(_patch->path(), name); if (offset != 0) { std::stringstream ss; ss << name << "_" << offset; diff --git a/src/libs/serialisation/Parser.cpp b/src/libs/serialisation/Parser.cpp index a9228f50..c9592fc4 100644 --- a/src/libs/serialisation/Parser.cpp +++ b/src/libs/serialisation/Parser.cpp @@ -96,7 +96,13 @@ Parser::parse_string( else cout << "[Parser] Parsing all objects found in string (base " << base_uri << ")" << endl; - return parse(world, target, model, base_uri, object_uri, parent, symbol, data);; + bool ret = parse(world, target, model, base_uri, object_uri, parent, symbol, data);; + if (ret) { + const Glib::ustring subject = Glib::ustring("<") + base_uri + Glib::ustring(">"); + parse_connections(world, target, model, base_uri, subject, parent ? parent.get() : "/"); + } + + return ret; } @@ -366,31 +372,7 @@ Parser::parse_patch( created.clear(); - /* Connections */ - query = Redland::Query(*world->rdf_world, Glib::ustring( - "SELECT DISTINCT ?src ?dst WHERE {\n") + - subject + " ingen:connection ?connection .\n" - "?connection ingen:source ?src ;\n" - " ingen:destination ?dst .\n" - "}"); - - results = query.run(*world->rdf_world, model, base_uri); - for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { - string src_path = patch_path.base() + uri_relative_to_base(base_uri, (*i)["src"].to_string()); - if (!Path::is_valid(src_path)) { - cerr << "ERROR: Invalid path in connection: " << src_path << endl; - continue; - } - - string dst_path = patch_path.base() + uri_relative_to_base(base_uri, (*i)["dst"].to_string()); - if (!Path::is_valid(dst_path)) { - cerr << "ERROR: Invalid path in connection: " << dst_path << endl; - continue; - } - - target->connect(src_path, dst_path); - } - + parse_connections(world, target, model, base_uri, subject, patch_path); parse_variables(world, target, model, base_uri, subject, patch_path, data); /* Enable */ @@ -446,6 +428,42 @@ Parser::parse_node( } +bool +Parser::parse_connections( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + const Glib::ustring& subject, + const Raul::Path& parent) +{ + Redland::Query query(*world->rdf_world, Glib::ustring( + "SELECT DISTINCT ?src ?dst WHERE {\n") + /*+ subject*/ + /*"?foo ingen:connection ?connection .\n"*/ + "?connection ingen:source ?src ;\n" + " ingen:destination ?dst .\n" + "}"); + + Redland::Query::Results results = query.run(*world->rdf_world, model, base_uri); + for (Redland::Query::Results::iterator i = results.begin(); i != results.end(); ++i) { + string src_path = parent.base() + uri_relative_to_base(base_uri, (*i)["src"].to_string()); + if (!Path::is_valid(src_path)) { + cerr << "ERROR: Invalid path in connection: " << src_path << endl; + continue; + } + + string dst_path = parent.base() + uri_relative_to_base(base_uri, (*i)["dst"].to_string()); + if (!Path::is_valid(dst_path)) { + cerr << "ERROR: Invalid path in connection: " << dst_path << endl; + continue; + } + + target->connect(src_path, dst_path); + } + + return true; +} + bool Parser::parse_variables( @@ -457,7 +475,6 @@ Parser::parse_variables( Raul::Path path, boost::optional data=boost::optional()) { - /* Parse variables */ Redland::Query query = Redland::Query(*world->rdf_world, Glib::ustring( "SELECT DISTINCT ?varkey ?varval WHERE {\n") + subject + " ingen:variable ?variable .\n" diff --git a/src/libs/serialisation/Parser.hpp b/src/libs/serialisation/Parser.hpp index 948a6248..1bc7245e 100644 --- a/src/libs/serialisation/Parser.hpp +++ b/src/libs/serialisation/Parser.hpp @@ -107,6 +107,15 @@ private: const Glib::ustring& subject, Raul::Path path, boost::optional data); + + bool parse_connections( + Ingen::Shared::World* world, + Ingen::Shared::CommonInterface* target, + Redland::Model& model, + const Glib::ustring& base_uri, + const Glib::ustring& subject, + const Raul::Path& parent); + }; diff --git a/src/libs/shared/Builder.cpp b/src/libs/shared/Builder.cpp index 83e0f3d2..2b5e954c 100644 --- a/src/libs/shared/Builder.cpp +++ b/src/libs/shared/Builder.cpp @@ -41,24 +41,27 @@ Builder::build(SharedPtr object) SharedPtr patch = PtrCast(object); if (patch) { if (patch->path() != "/") - _interface.new_patch(patch->path() + "_copy", patch->internal_polyphony()); + _interface.new_patch(patch->path(), patch->internal_polyphony()); build_object(object); for (Patch::Connections::const_iterator i = patch->connections().begin(); - i != patch->connections().end(); ++i) + i != patch->connections().end(); ++i) { + cout << "BUILDER CONNECTION: " <<(*i)->src_port_path() + << " -> " << (*i)->dst_port_path() << endl; _interface.connect((*i)->src_port_path(), (*i)->dst_port_path()); + } return; } SharedPtr node = PtrCast(object); if (node) { - _interface.new_node(node->path() + "_copy", node->plugin()->uri()); + _interface.new_node(node->path(), node->plugin()->uri()); build_object(object); return; } SharedPtr port = PtrCast(object); if (port) { - _interface.new_port(port->path() + "_copy", port->index(), port->type().uri(), !port->is_input()); + _interface.new_port(port->path(), port->index(), port->type().uri(), !port->is_input()); build_object(object); return; } @@ -70,11 +73,11 @@ Builder::build_object(SharedPtr object) { for (GraphObject::Variables::const_iterator i = object->variables().begin(); i != object->variables().end(); ++i) - _interface.set_variable(object->path() + "_copy", i->first, i->second); + _interface.set_variable(object->path(), i->first, i->second); for (GraphObject::Properties::const_iterator i = object->properties().begin(); i != object->properties().end(); ++i) - _interface.set_property(object->path() + "_copy", i->first, i->second); + _interface.set_property(object->path(), i->first, i->second); } diff --git a/src/libs/shared/ClashAvoider.cpp b/src/libs/shared/ClashAvoider.cpp new file mode 100644 index 00000000..3647f023 --- /dev/null +++ b/src/libs/shared/ClashAvoider.cpp @@ -0,0 +1,154 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * 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 "ClashAvoider.hpp" +#include "Store.hpp" + +using namespace std; +using namespace Raul; + +namespace Ingen { +namespace Shared { + + +const Raul::Path& +ClashAvoider::map_path(const Raul::Path& in) +{ + SymbolMap::iterator m = _symbol_map.find(in); + if (m != _symbol_map.end()) { + return m->second; + } else { + typedef std::pair InsertRecord; + Store::iterator s = _store.find(in); + + // No clash, use symbol unmodified + if (s == _store.end()) { + InsertRecord i = _symbol_map.insert(make_pair(in, in)); + cout << i.first->second << endl; + return i.first->second; + } else { + + // See if the parent is mapped + Path parent = in.parent(); + do { + SymbolMap::iterator p = _symbol_map.find(parent); + if (p != _symbol_map.end()) { + const Path mapped = p->second.base() + in.substr(parent.base().length()); + InsertRecord i = _symbol_map.insert(make_pair(in, mapped)); + return i.first->second; + } + } while (parent != "/"); + + // Append _2 _3 etc until an unused symbol is found + string base_name = in.name(); + unsigned offset = 0; + if (sscanf(base_name.c_str(), "%*s_%u", &offset) == 1) + base_name = base_name.substr(0, base_name.find_last_of("_")); + else + offset = _store.child_name_offset(in.parent(), in.name()); + + assert(offset != 0); // shouldn't have been a clash, then... + std::stringstream ss; + ss << in << "_" << offset; + InsertRecord i = _symbol_map.insert(make_pair(in, ss.str())); + assert(_store.find(i.first->second) == _store.end()); + cout << i.first->second << endl; + return i.first->second; + } + } +} + + +void +ClashAvoider::new_patch(const std::string& path, + uint32_t poly) +{ + _target.new_patch(map_path(path), poly); +} + + +void +ClashAvoider::new_node(const std::string& path, + const std::string& plugin_uri) +{ + _target.new_node(map_path(path), plugin_uri); +} + + +void +ClashAvoider::new_port(const std::string& path, + uint32_t index, + const std::string& data_type, + bool is_output) +{ + _target.new_port(map_path(path), index, data_type, is_output); +} + + +void +ClashAvoider::connect(const std::string& src_port_path, + const std::string& dst_port_path) +{ + _target.connect(map_path(src_port_path), map_path(dst_port_path)); +} + + +void +ClashAvoider::disconnect(const std::string& src_port_path, + const std::string& dst_port_path) +{ + _target.disconnect(map_path(src_port_path), map_path(dst_port_path)); +} + + +void +ClashAvoider::set_variable(const std::string& subject_path, + const std::string& predicate, + const Raul::Atom& value) +{ + _target.set_variable(map_path(subject_path), predicate, value); +} + + +void +ClashAvoider::set_property(const std::string& subject_path, + const std::string& predicate, + const Raul::Atom& value) +{ + _target.set_property(map_path(subject_path), predicate, value); +} + + +void +ClashAvoider::set_port_value(const std::string& port_path, + const Raul::Atom& value) +{ + _target.set_port_value(map_path(port_path), value); +} + + +void +ClashAvoider::set_voice_value(const std::string& port_path, + uint32_t voice, + const Raul::Atom& value) +{ + _target.set_voice_value(map_path(port_path), voice, value); +} + + +} // namespace Shared +} // namespace Ingen diff --git a/src/libs/shared/ClashAvoider.hpp b/src/libs/shared/ClashAvoider.hpp new file mode 100644 index 00000000..05b2cd6c --- /dev/null +++ b/src/libs/shared/ClashAvoider.hpp @@ -0,0 +1,96 @@ +/* This file is part of Ingen. + * Copyright (C) 2008 Dave Robillard + * + * 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 CLASHAVOIDER_H +#define CLASHAVOIDER_H + +#include +#include +#include +#include +#include +#include "interface/CommonInterface.hpp" + +namespace Ingen { +namespace Shared { + +class Store; + + +/** A wrapper for a CommonInterface that creates objects but possibly maps + * symbol names to avoid clashes with the existing objects in a store. + */ +class ClashAvoider : public CommonInterface +{ +public: + ClashAvoider(Store& store, CommonInterface& target) + : _store(store), _target(target) {} + + // Bundles + void bundle_begin() { _target.bundle_begin(); } + void bundle_end() { _target.bundle_end(); } + + // Object commands + + void new_patch(const std::string& path, + uint32_t poly); + + void new_node(const std::string& path, + const std::string& plugin_uri); + + void new_port(const std::string& path, + uint32_t index, + const std::string& data_type, + bool is_output); + + void connect(const std::string& src_port_path, + const std::string& dst_port_path); + + void disconnect(const std::string& src_port_path, + const std::string& dst_port_path); + + void set_variable(const std::string& subject_path, + const std::string& predicate, + const Raul::Atom& value); + + void set_property(const std::string& subject_path, + const std::string& predicate, + const Raul::Atom& value); + + void set_port_value(const std::string& port_path, + const Raul::Atom& value); + + void set_voice_value(const std::string& port_path, + uint32_t voice, + const Raul::Atom& value); + +private: + const Raul::Path& map_path(const Raul::Path& in); + + Store& _store; + CommonInterface& _target; + + typedef std::map SymbolMap; + SymbolMap _symbol_map; +}; + + +} // namespace Shared +} // namespace Ingen + +#endif // CLASHAVOIDER_H + diff --git a/src/libs/shared/Makefile.am b/src/libs/shared/Makefile.am index fcb261c8..c26c98a2 100644 --- a/src/libs/shared/Makefile.am +++ b/src/libs/shared/Makefile.am @@ -10,6 +10,8 @@ libingen_shared_la_CXXFLAGS = \ libingen_shared_la_SOURCES = \ Builder.cpp \ Builder.hpp \ + ClashAvoider.cpp \ + ClashAvoider.hpp \ LV2Features.cpp \ LV2Features.hpp \ LV2URIMap.cpp \ diff --git a/src/libs/shared/Store.cpp b/src/libs/shared/Store.cpp index f77be312..fea18b6d 100644 --- a/src/libs/shared/Store.cpp +++ b/src/libs/shared/Store.cpp @@ -78,6 +78,29 @@ Store::find_child(SharedPtr parent, const string& child_nam else return SharedPtr(); } + + +unsigned +Store::child_name_offset(const Raul::Path& parent, + const Raul::Symbol& symbol) +{ + unsigned offset = 0; + + while (true) { + std::stringstream ss; + ss << symbol; + if (offset > 0) + ss << "_" << offset; + if (find(parent.base() + ss.str()) == end()) + break; + else if (offset == 0) + offset = 2; + else + ++offset; + } + + return offset; +} } // namespace Shared diff --git a/src/libs/shared/Store.hpp b/src/libs/shared/Store.hpp index 573c4837..bbefdc5c 100644 --- a/src/libs/shared/Store.hpp +++ b/src/libs/shared/Store.hpp @@ -42,6 +42,9 @@ public: SharedPtr find_child(SharedPtr parent, const std::string& child_name) const; + + unsigned child_name_offset(const Raul::Path& parent, + const Raul::Symbol& symbol); Glib::RWLock& lock() { return _lock; } -- cgit v1.2.1