/* This file is part of Ingen. * Copyright (C) 2007 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 #include "ClientStore.hpp" #include "ObjectModel.hpp" #include "PatchModel.hpp" #include "NodeModel.hpp" #include "PortModel.hpp" #include "PluginModel.hpp" #include "PatchModel.hpp" #include "SigClientInterface.hpp" using namespace std; using namespace Raul; namespace Ingen { namespace Client { ClientStore::ClientStore(SharedPtr engine, SharedPtr emitter) : _engine(engine) , _emitter(emitter) , _plugins(new Plugins()) { _handle_orphans = (engine && emitter); if (!emitter) return; emitter->signal_object_destroyed.connect(sigc::mem_fun(this, &ClientStore::destroy)); emitter->signal_object_renamed.connect(sigc::mem_fun(this, &ClientStore::rename)); emitter->signal_new_plugin.connect(sigc::mem_fun(this, &ClientStore::new_plugin)); emitter->signal_new_patch.connect(sigc::mem_fun(this, &ClientStore::new_patch)); emitter->signal_new_node.connect(sigc::mem_fun(this, &ClientStore::new_node)); emitter->signal_new_port.connect(sigc::mem_fun(this, &ClientStore::new_port)); emitter->signal_patch_cleared.connect(sigc::mem_fun(this, &ClientStore::patch_cleared)); emitter->signal_connection.connect(sigc::mem_fun(this, &ClientStore::connect)); emitter->signal_disconnection.connect(sigc::mem_fun(this, &ClientStore::disconnect)); emitter->signal_variable_change.connect(sigc::mem_fun(this, &ClientStore::set_variable)); emitter->signal_property_change.connect(sigc::mem_fun(this, &ClientStore::set_property)); emitter->signal_port_value.connect(sigc::mem_fun(this, &ClientStore::set_port_value)); emitter->signal_voice_value.connect(sigc::mem_fun(this, &ClientStore::set_voice_value)); emitter->signal_port_activity.connect(sigc::mem_fun(this, &ClientStore::port_activity)); } void ClientStore::clear() { Store::clear(); _plugins->clear(); } void ClientStore::add_plugin_orphan(SharedPtr node) { if (!_handle_orphans) return; Raul::Table > >::iterator spawn = _plugin_orphans.find(node->plugin_uri()); if (spawn != _plugin_orphans.end()) { spawn->second.push_back(node); } else { cerr << "WARNING: Orphans of plugin " << node->plugin_uri() << " received" << endl; _engine->request_plugin(node->plugin_uri()); list > l; l.push_back(node); _plugin_orphans[node->plugin_uri()] = l; } } void ClientStore::resolve_plugin_orphans(SharedPtr plugin) { if (!_handle_orphans) return; Raul::Table > >::iterator n = _plugin_orphans.find(plugin->uri()); if (n != _plugin_orphans.end()) { list > spawn = n->second; // take a copy cerr << "Missing dependant " << plugin->uri() << " received" << endl; _plugin_orphans.erase(plugin->uri()); // prevent infinite recursion for (list >::iterator i = spawn.begin(); i != spawn.end(); ++i) { (*i)->_plugin = plugin; //add_object(*i); } } } void ClientStore::add_connection_orphan(std::pair orphan) { // 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); } void ClientStore::resolve_connection_orphans(SharedPtr port) { if (!_handle_orphans) return; assert(port->parent()); for (list< pair >::iterator c = _connection_orphans.begin(); c != _connection_orphans.end(); ) { list< pair >::iterator next = c; ++next; if (c->first == port->path() || c->second == port->path()) { cerr << "Missing dependant (" << c->first << " -> " << c->second << ") received" << endl; bool success = attempt_connection(c->first, c->second); if (success) _connection_orphans.erase(c); } c = next; } } void ClientStore::add_orphan(SharedPtr child) { if (!_handle_orphans) return; cerr << "WARNING: Orphan object " << child->path() << " received." << endl; Raul::PathTable > >::iterator children = _orphans.find(child->path().parent()); _engine->request_object(child->path().parent()); if (children != _orphans.end()) { children->second.push_back(child); } else { list > l; l.push_back(child); _orphans.insert(make_pair(child->path().parent(), l)); } } void ClientStore::add_variable_orphan(const Path& subject_path, const string& predicate, const Atom& value) { if (!_handle_orphans) return; Raul::PathTable > >::iterator orphans = _variable_orphans.find(subject_path); _engine->request_object(subject_path); if (orphans != _variable_orphans.end()) { orphans->second.push_back(std::pair(predicate, value)); } else { list > l; l.push_back(std::pair(predicate, value)); _variable_orphans[subject_path] = l; } } void ClientStore::resolve_variable_orphans(SharedPtr subject) { if (!_handle_orphans) return; Raul::PathTable > >::iterator v = _variable_orphans.find(subject->path()); if (v != _variable_orphans.end()) { list > values = v->second; // take a copy _variable_orphans.erase(subject->path()); cerr << "Missing dependant " << subject->path() << " received" << endl; for (list >::iterator i = values.begin(); i != values.end(); ++i) { subject->set_variable(i->first, i->second); } } } void ClientStore::resolve_orphans(SharedPtr parent) { if (!_handle_orphans) return; Raul::PathTable > >::iterator c = _orphans.find(parent->path()); if (c != _orphans.end()) { list > children = c->second; // take a copy _orphans.erase(parent->path()); // prevent infinite recursion for (list >::iterator i = children.begin(); i != children.end(); ++i) { add_object(*i); } } } void ClientStore::add_object(SharedPtr 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()) { PtrCast(existing->second)->set(object); } else { if (object->path() != "/") { SharedPtr parent = this->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); resolve_variable_orphans(parent); resolve_orphans(parent); SharedPtr port = PtrCast(object); if (port) resolve_connection_orphans(port); } else { add_orphan(object); } } else { (*this)[object->path()] = object; signal_new_object.emit(object); } } /*cout << "[Store] Added " << object->path() << " {" << endl; for (iterator i = begin(); i != end(); ++i) { cout << "\t" << i->first << endl; } cout << "}" << endl;*/ } SharedPtr ClientStore::remove_object(const Path& path) { iterator i = find(path); if (i != end()) { assert((*i).second->path() == path); SharedPtr result = PtrCast((*i).second); assert(result); //erase(i); iterator descendants_end = find_descendants_end(i); SharedPtr removed = yank(i, descendants_end); /*cout << "[Store] Removing " << i->first << " {" << endl; for (iterator i = removed.begin(); i != removed.end(); ++i) { cout << "\t" << i->first << endl; } cout << "}" << endl;*/ if (result) result->signal_destroyed.emit(); if (result->path() != "/") { assert(result->parent()); SharedPtr parent = this->object(result->path().parent()); if (parent) { parent->remove_child(result); } } assert(!object(path)); return result; } else { return SharedPtr(); } } SharedPtr ClientStore::plugin(const string& uri) { assert(uri.length() > 0); Plugins::iterator i = _plugins->find(uri); if (i == _plugins->end()) return SharedPtr(); else return (*i).second; } SharedPtr ClientStore::object(const Path& path) { assert(path.length() > 0); iterator i = find(path); if (i == end()) { return SharedPtr(); } else { SharedPtr model = PtrCast(i->second); assert(model); assert(model->path() == "/" || model->parent()); return model; } } void ClientStore::add_plugin(SharedPtr pm) { // FIXME: dupes? merge, like with objects? (*_plugins)[pm->uri()] = pm; signal_new_plugin(pm); //cerr << "Plugin: " << pm->uri() << ", # plugins: " << _plugins->size() << endl; } /* ****** Signal Handlers ******** */ void ClientStore::destroy(const std::string& path) { SharedPtr removed = remove_object(path); removed.reset(); //cerr << "[ClientStore] removed object " << path << ", count: " << removed.use_count(); } void ClientStore::rename(const Path& old_path, const Path& new_path) { iterator parent = find(old_path); if (parent == end()) { cerr << "[Store] Failed to find object " << old_path << " to rename." << endl; return; } iterator descendants_end = find_descendants_end(parent); SharedPtr< Table > > removed = yank(parent, descendants_end); assert(removed->size() > 0); for (Table >::iterator i = removed->begin(); i != removed->end(); ++i) { const Path& child_old_path = i->first; assert(Path::descendant_comparator(old_path, child_old_path)); Path child_new_path; if (child_old_path == old_path) child_new_path = new_path; else child_new_path = new_path.base() + child_old_path.substr(old_path.length()+1); cerr << "[Store] Renamed " << child_old_path << " -> " << child_new_path << endl; PtrCast(i->second)->set_path(child_new_path); i->first = child_new_path; } cram(*removed.get()); //cerr << "[Store] Table:" << endl; //for (size_t i=0; i < removed.size(); ++i) { // cerr << removed[i].first << "\t\t: " << removed[i].second << endl; //} /*for (iterator i = begin(); i != end(); ++i) { cerr << i->first << "\t\t: " << i->second << endl; }*/ } void ClientStore::new_plugin(const string& uri, const string& type_uri, const string& symbol, const string& name) { SharedPtr p(new PluginModel(uri, type_uri, symbol, name)); add_plugin(p); resolve_plugin_orphans(p); } void ClientStore::new_patch(const string& path, uint32_t poly) { SharedPtr p(new PatchModel(path, poly)); add_object(p); } void ClientStore::new_node(const string& path, const string& plugin_uri) { SharedPtr plug = plugin(plugin_uri); if (!plug) { SharedPtr n(new NodeModel(plugin_uri, path)); add_plugin_orphan(n); add_object(n); } else { SharedPtr n(new NodeModel(plug, path)); add_object(n); } } void ClientStore::new_port(const string& path, uint32_t index, const string& type, bool is_output) { PortModel::Direction pdir = is_output ? PortModel::OUTPUT : PortModel::INPUT; SharedPtr p(new PortModel(path, index, type, pdir)); add_object(p); if (p->parent()) resolve_connection_orphans(p); } void ClientStore::patch_cleared(const Path& path) { iterator i = find(path); if (i != end()) { assert((*i).second->path() == path); SharedPtr patch = PtrCast(i->second); iterator first_descendant = i; ++first_descendant; iterator descendants_end = find_descendants_end(i); SharedPtr< Table > > removed = yank(first_descendant, descendants_end); for (iterator i = removed->begin(); i != removed->end(); ++i) { SharedPtr model = PtrCast(i->second); assert(model); model->signal_destroyed.emit(); if (model->parent() == patch) patch->remove_child(model); } } else { cerr << "[Store] Unable to find patch " << path << " to clear." << endl; } } void ClientStore::set_variable(const string& subject_path, const string& predicate, const Atom& value) { SharedPtr subject = object(subject_path); if (!value.is_valid()) { cerr << "ERROR: variable '" << predicate << "' has no type" << endl; } else if (subject) { subject->set_variable(predicate, value); } else { add_variable_orphan(subject_path, predicate, value); cerr << "WARNING: variable for unknown object " << subject_path << endl; } } void ClientStore::set_property(const string& subject_path, const string& predicate, const Atom& value) { SharedPtr subject = object(subject_path); if (!value.is_valid()) { cerr << "ERROR: property '" << predicate << "' has no type" << endl; } else if (subject) { subject->set_property(predicate, value); } else { cerr << "WARNING: property for unknown object " << subject_path << " lost. Client must refresh!" << endl; } } void ClientStore::set_port_value(const string& port_path, const Raul::Atom& value) { SharedPtr port = PtrCast(object(port_path)); if (port) port->value(value); else cerr << "ERROR: control change for nonexistant port " << port_path << endl; } void ClientStore::set_voice_value(const string& port_path, uint32_t voice, const Raul::Atom& value) { SharedPtr port = PtrCast(object(port_path)); if (port) port->value(voice, value); else cerr << "ERROR: poly control change for nonexistant port " << port_path << endl; } void ClientStore::port_activity(const Path& port_path) { SharedPtr port = PtrCast(object(port_path)); if (port) port->signal_activity.emit(); else cerr << "ERROR: activity for nonexistant port " << port_path << endl; } SharedPtr ClientStore::connection_patch(const Path& src_port_path, const Path& dst_port_path) { SharedPtr patch; if (src_port_path.parent() == dst_port_path.parent()) patch = PtrCast(this->object(src_port_path.parent())); if (!patch && src_port_path.parent() == dst_port_path.parent().parent()) patch = PtrCast(this->object(src_port_path.parent())); if (!patch && src_port_path.parent().parent() == dst_port_path.parent()) patch = PtrCast(this->object(dst_port_path.parent())); if (!patch) patch = PtrCast(this->object(src_port_path.parent().parent())); if (!patch) cerr << "ERROR: Unable to find connection patch " << src_port_path << " -> " << dst_port_path << endl; return patch; } bool ClientStore::attempt_connection(const Path& src_port_path, const Path& dst_port_path, bool add_orphan) { SharedPtr src_port = PtrCast(object(src_port_path)); SharedPtr dst_port = PtrCast(object(dst_port_path)); if (src_port && dst_port) { assert(src_port->parent()); assert(dst_port->parent()); SharedPtr patch = connection_patch(src_port_path, dst_port_path); assert(patch); SharedPtr cm(new ConnectionModel(src_port, dst_port)); src_port->connected_to(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; } void ClientStore::connect(const string& src_port_path, const string& dst_port_path) { attempt_connection(src_port_path, dst_port_path, true); } void ClientStore::disconnect(const string& src_port_path, const string& dst_port_path) { // Find the ports and create a ConnectionModel just to get at the parent path // finding logic in ConnectionModel. So I'm lazy. SharedPtr src_port = PtrCast(object(src_port_path)); SharedPtr dst_port = PtrCast(object(dst_port_path)); if (src_port) src_port->disconnected_from(dst_port); else cerr << "WARNING: Disconnection from nonexistant src port " << src_port_path << endl; if (dst_port) dst_port->disconnected_from(dst_port); else cerr << "WARNING: Disconnection from nonexistant dst port " << dst_port_path << endl; SharedPtr patch = connection_patch(src_port_path, dst_port_path); if (patch) patch->remove_connection(src_port_path, dst_port_path); else cerr << "ERROR: disconnection in nonexistant patch: " << src_port_path << " -> " << dst_port_path << endl; } } // namespace Client } // namespace Ingen