/* This file is part of Ingen. Copyright 2007-2016 David Robillard Ingen is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or 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 Affero General Public License for details. You should have received a copy of the GNU Affero General Public License along with Ingen. If not, see . */ #include "ingen/Log.hpp" #include "ingen/client/ArcModel.hpp" #include "ingen/client/BlockModel.hpp" #include "ingen/client/ClientStore.hpp" #include "ingen/client/GraphModel.hpp" #include "ingen/client/ObjectModel.hpp" #include "ingen/client/ParameterModel.hpp" #include "ingen/client/PluginModel.hpp" #include "ingen/client/PortModel.hpp" #include "ingen/client/SigClientInterface.hpp" using namespace std; namespace Ingen { namespace Client { ClientStore::ClientStore(URIs& uris, Log& log, SPtr emitter) : _uris(uris) , _log(log) , _emitter(emitter) , _plugins(new Plugins()) { if (!emitter) return; #define CONNECT(signal, method) \ emitter->signal_##signal().connect( \ sigc::mem_fun(this, &ClientStore::method)); CONNECT(object_deleted, del); CONNECT(object_moved, move); CONNECT(put, put); CONNECT(delta, delta); CONNECT(connection, connect); CONNECT(disconnection, disconnect); CONNECT(disconnect_all, disconnect_all); CONNECT(property_change, set_property); } void ClientStore::clear() { Store::clear(); _plugins->clear(); } void ClientStore::add_object(SPtr object) { // If we already have "this" object, merge the existing one into the new // one (with precedence to the new values). iterator existing = find(object->path()); if (existing != end()) { dynamic_ptr_cast(existing->second)->set(object); } else { if (!object->path().is_root()) { SPtr parent = _object(object->path().parent()); if (parent) { assert(object->path().is_child_of(parent->path())); object->set_parent(parent); parent->add_child(object); assert(parent && (object->parent() == parent)); (*this)[object->path()] = object; _signal_new_object.emit(object); } else { _log.error(fmt("Object %1% with no parent\n") % object->path()); } } else { (*this)[object->path()] = object; _signal_new_object.emit(object); } } for (auto p : object->properties()) object->signal_property().emit(p.first, p.second); } SPtr ClientStore::remove_object(const Raul::Path& path) { // Find the object, the "top" of the tree to remove const iterator top = find(path); if (top == end()) { return SPtr(); } SPtr object = dynamic_ptr_cast(top->second); // Remove object and any adjacent arcs from parent if applicable if (object && object->parent()) { SPtr port = dynamic_ptr_cast(object); if (port && dynamic_ptr_cast(port->parent())) { disconnect_all(port->parent()->path(), path); if (port->parent()->parent()) { disconnect_all(port->parent()->parent()->path(), path); } } else if (port && port->parent()->parent()) { disconnect_all(port->parent()->parent()->path(), path); } else { disconnect_all(object->parent()->path(), path); } object->parent()->remove_child(object); } // Remove the object and all its descendants Objects removed; remove(top, removed); // Notify everything that needs to know this object has been removed if (object) { object->signal_destroyed().emit(); } return object; } SPtr ClientStore::_plugin(const Raul::URI& uri) { const Plugins::iterator i = _plugins->find(uri); return (i == _plugins->end()) ? SPtr() : (*i).second; } SPtr ClientStore::_plugin(const Atom& uri) { /* FIXME: SHould probably be stored with URIs rather than strings, to make this a fast case. */ const Plugins::iterator i = _plugins->find(Raul::URI(_uris.forge.str(uri, false))); return (i == _plugins->end()) ? SPtr() : (*i).second; } SPtr ClientStore::plugin(const Raul::URI& uri) const { return const_cast(this)->_plugin(uri); } SPtr ClientStore::_object(const Raul::Path& path) { const iterator i = find(path); if (i == end()) { return SPtr(); } else { SPtr model = dynamic_ptr_cast(i->second); assert(model); assert(model->path().is_root() || model->parent()); return model; } } SPtr ClientStore::object(const Raul::Path& path) const { return const_cast(this)->_object(path); } SPtr ClientStore::_resource(const Raul::URI& uri) { if (Node::uri_is_path(uri)) { return _object(Node::uri_to_path(uri)); } else { return _plugin(uri); } } SPtr ClientStore::resource(const Raul::URI& uri) const { return const_cast(this)->_resource(uri); } void ClientStore::add_plugin(SPtr pm) { SPtr existing = _plugin(pm->uri()); if (existing) { existing->set(pm); } else { _plugins->insert(make_pair(pm->uri(), pm)); _signal_new_plugin.emit(pm); } } /* ****** Signal Handlers ******** */ void ClientStore::del(const Raul::URI& uri) { if (Node::uri_is_path(uri)) { remove_object(Node::uri_to_path(uri)); } else { Plugins::iterator p = _plugins->find(uri); if (p != _plugins->end()) { _plugins->erase(p); _signal_plugin_deleted.emit(uri); } } } void ClientStore::copy(const Raul::URI& old_uri, const Raul::URI& new_uri) { _log.error("Client store copy unsupported\n"); } void ClientStore::move(const Raul::Path& old_path, const Raul::Path& new_path) { const iterator top = find(old_path); if (top != end()) { rename(top, new_path); } } void ClientStore::put(const Raul::URI& uri, const Resource::Properties& properties, Resource::Graph ctx) { typedef Resource::Properties::const_iterator Iterator; bool is_graph, is_block, is_port, is_parameter, is_output; Resource::type(uris(), properties, is_graph, is_block, is_port, is_parameter, is_output); // Check for specially handled types const Iterator t = properties.find(_uris.rdf_type); if (t != properties.end()) { const Atom& type(t->second); if (_uris.pset_Preset == type) { const Iterator p = properties.find(_uris.lv2_appliesTo); const Iterator l = properties.find(_uris.rdfs_label); SPtr plug; if (p == properties.end()) { _log.error(fmt("Preset <%1%> with no plugin\n") % uri.c_str()); } else if (l == properties.end()) { _log.error(fmt("Preset <%1%> with no label\n") % uri.c_str()); } else if (l->second.type() != _uris.forge.String) { _log.error(fmt("Preset <%1%> label is not a string\n") % uri.c_str()); } else if (!(plug = _plugin(p->second))) { _log.error(fmt("Preset <%1%> for unknown plugin %2%\n") % uri.c_str() % _uris.forge.str(p->second)); } else { plug->add_preset(uri, l->second.ptr()); } return; } else if (_uris.ingen_Graph == type) { is_graph = true; } else if (_uris.ingen_Internal == type || _uris.lv2_Plugin == type) { SPtr p(new PluginModel(uris(), uri, type, properties)); add_plugin(p); return; } } if (!Node::uri_is_path(uri)) { _log.error(fmt("Put for unknown subject <%1%>\n") % uri.c_str()); return; } const Raul::Path path(Node::uri_to_path(uri)); SPtr obj = dynamic_ptr_cast(_object(path)); if (obj) { obj->set_properties(properties); return; } if (path.is_root()) { is_graph = true; } if (is_graph) { SPtr model(new GraphModel(uris(), path)); model->set_properties(properties); add_object(model); } else if (is_block) { Iterator p = properties.find(_uris.lv2_prototype); if (p == properties.end()) { p = properties.find(_uris.ingen_prototype); } SPtr plug; if (p->second.is_valid() && (p->second.type() == _uris.forge.URI || p->second.type() == _uris.forge.URID)) { const Raul::URI uri(_uris.forge.str(p->second, false)); if (!(plug = _plugin(uri))) { plug = SPtr( new PluginModel(uris(), uri, Atom(), Resource::Properties())); add_plugin(plug); } SPtr bm(new BlockModel(uris(), plug, path)); bm->set_properties(properties); add_object(bm); } else { _log.warn(fmt("Block %1% has no prototype\n") % path.c_str()); } } else if (is_port) { PortModel::Direction pdir = (is_output) ? PortModel::Direction::OUTPUT : PortModel::Direction::INPUT; uint32_t index = 0; const Iterator i = properties.find(_uris.lv2_index); if (i != properties.end() && i->second.type() == _uris.forge.Int) { index = i->second.get(); } SPtr p(new PortModel(uris(), path, index, pdir)); p->set_properties(properties); add_object(p); } else if (is_parameter) { SPtr p(new ParameterModel(uris(), path)); p->set_properties(properties); add_object(p); } else { _log.warn(fmt("Ignoring %1% of unknown type\n") % path.c_str()); } } void ClientStore::delta(const Raul::URI& uri, const Resource::Properties& remove, const Resource::Properties& add) { if (uri == Raul::URI("ingen:/clients/this")) { // Client property, which we don't store (yet?) return; } if (!Node::uri_is_path(uri)) { _log.error(fmt("Delta for unknown subject <%1%>\n") % uri.c_str()); return; } const Raul::Path path(Node::uri_to_path(uri)); SPtr obj = _object(path); if (obj) { obj->remove_properties(remove); obj->add_properties(add); } else { _log.warn(fmt("Failed to find object `%1%'\n") % path.c_str()); } } void ClientStore::set_property(const Raul::URI& subject_uri, const Raul::URI& predicate, const Atom& value) { if (subject_uri == Raul::URI("ingen:/engine")) { _log.info(fmt("Engine property <%1%> = %2%\n") % predicate.c_str() % _uris.forge.str(value)); return; } SPtr subject = _resource(subject_uri); if (subject) { if (predicate == _uris.ingen_activity) { /* Activity is transient, trigger any live actions (like GUI blinkenlights) but do not store the property. */ subject->on_property(predicate, value); } else { subject->set_property(predicate, value); } } else { SPtr plugin = _plugin(subject_uri); if (plugin) { plugin->set_property(predicate, value); } else if (predicate != _uris.ingen_activity) { _log.warn(fmt("Property <%1%> for unknown object %2%\n") % predicate.c_str() % subject_uri.c_str()); } } } SPtr ClientStore::connection_graph(const Raul::Path& tail_path, const Raul::Path& head_path) { SPtr graph; if (tail_path.parent() == head_path.parent()) graph = dynamic_ptr_cast(_object(tail_path.parent())); if (!graph && tail_path.parent() == head_path.parent().parent()) graph = dynamic_ptr_cast(_object(tail_path.parent())); if (!graph && tail_path.parent().parent() == head_path.parent()) graph = dynamic_ptr_cast(_object(head_path.parent())); if (!graph) graph = dynamic_ptr_cast(_object(tail_path.parent().parent())); if (!graph) _log.error(fmt("Unable to find graph for arc %1% => %2%\n") % tail_path % head_path); return graph; } bool ClientStore::attempt_connection(const Raul::Path& tail_path, const Raul::Path& head_path) { SPtr tail = dynamic_ptr_cast(_object(tail_path)); SPtr head = dynamic_ptr_cast(_object(head_path)); if (tail && head) { SPtr graph = connection_graph(tail_path, head_path); SPtr arc(new ArcModel(tail, head)); tail->connected_to(head); head->connected_to(tail); graph->add_arc(arc); return true; } else { _log.warn(fmt("Failed to connect %1% => %2%\n") % tail_path % head_path); return false; } } void ClientStore::connect(const Raul::Path& src_path, const Raul::Path& dst_path) { attempt_connection(src_path, dst_path); } void ClientStore::disconnect(const Raul::Path& src_path, const Raul::Path& dst_path) { SPtr tail = dynamic_ptr_cast(_object(src_path)); SPtr head = dynamic_ptr_cast(_object(dst_path)); if (tail) tail->disconnected_from(head); if (head) head->disconnected_from(tail); SPtr graph = connection_graph(src_path, dst_path); if (graph) graph->remove_arc(tail.get(), head.get()); } void ClientStore::disconnect_all(const Raul::Path& parent_graph, const Raul::Path& path) { SPtr graph = dynamic_ptr_cast(_object(parent_graph)); SPtr object = _object(path); if (!graph || !object) { _log.error(fmt("Bad disconnect all notification %1% in %2%\n") % path % parent_graph); return; } const GraphModel::Arcs arcs = graph->arcs(); for (auto a : arcs) { SPtr arc = dynamic_ptr_cast(a.second); if (arc->tail()->parent() == object || arc->head()->parent() == object || arc->tail()->path() == path || arc->head()->path() == path) { arc->tail()->disconnected_from(arc->head()); arc->head()->disconnected_from(arc->tail()); graph->remove_arc(arc->tail().get(), arc->head().get()); } } } } // namespace Client } // namespace Ingen