/* This file is part of Ingen. Copyright (C) 2006 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 "DisconnectionEvent.h" #include #include "Responder.h" #include "Engine.h" #include "TypedConnection.h" #include "InputPort.h" #include "OutputPort.h" #include "Patch.h" #include "ClientBroadcaster.h" #include "Port.h" #include "Maid.h" #include "ObjectStore.h" #include "util/Path.h" using std::string; namespace Ingen { //// DisconnectionEvent //// DisconnectionEvent::DisconnectionEvent(Engine& engine, CountedPtr responder, SampleCount timestamp, const string& src_port_path, const string& dst_port_path) : QueuedEvent(engine, responder, timestamp), m_src_port_path(src_port_path), m_dst_port_path(dst_port_path), m_patch(NULL), m_src_port(NULL), m_dst_port(NULL), m_lookup(true), m_typed_event(NULL), m_error(NO_ERROR) { } DisconnectionEvent::DisconnectionEvent(Engine& engine, CountedPtr responder, SampleCount timestamp, Port* const src_port, Port* const dst_port) : QueuedEvent(engine, responder, timestamp), m_src_port_path(src_port->path()), m_dst_port_path(dst_port->path()), m_patch(src_port->parent_node()->parent_patch()), m_src_port(src_port), m_dst_port(dst_port), m_lookup(false), m_typed_event(NULL), m_error(NO_ERROR) { assert(src_port->is_output()); assert(dst_port->is_input()); assert(src_port->type() == dst_port->type()); assert(src_port->parent_node()->parent_patch() == dst_port->parent_node()->parent_patch()); } DisconnectionEvent::~DisconnectionEvent() { delete m_typed_event; } void DisconnectionEvent::pre_process() { if (m_lookup) { if (m_src_port_path.parent().parent() != m_dst_port_path.parent().parent() && m_src_port_path.parent() != m_dst_port_path.parent().parent() && m_src_port_path.parent().parent() != m_dst_port_path.parent()) { m_error = PARENT_PATCH_DIFFERENT; QueuedEvent::pre_process(); return; } /*m_patch = _engine.object_store()->find_patch(m_src_port_path.parent().parent()); if (m_patch == NULL) { m_error = PORT_NOT_FOUND; QueuedEvent::pre_process(); return; }*/ m_src_port = _engine.object_store()->find_port(m_src_port_path); m_dst_port = _engine.object_store()->find_port(m_dst_port_path); } if (m_src_port == NULL || m_dst_port == NULL) { m_error = PORT_NOT_FOUND; QueuedEvent::pre_process(); return; } if (m_src_port->type() != m_dst_port->type() || m_src_port->buffer_size() != m_dst_port->buffer_size()) { m_error = TYPE_MISMATCH; QueuedEvent::pre_process(); return; } // Create the typed event to actually do the work const DataType type = m_src_port->type(); if (type == DataType::FLOAT) { m_typed_event = new TypedDisconnectionEvent(_engine, _responder, _time, dynamic_cast*>(m_src_port), dynamic_cast*>(m_dst_port)); } else if (type == DataType::MIDI) { m_typed_event = new TypedDisconnectionEvent(_engine, _responder, _time, dynamic_cast*>(m_src_port), dynamic_cast*>(m_dst_port)); } else { m_error = TYPE_MISMATCH; QueuedEvent::pre_process(); return; } assert(m_typed_event); m_typed_event->pre_process(); assert(m_typed_event->is_prepared()); QueuedEvent::pre_process(); } void DisconnectionEvent::execute(SampleCount nframes, FrameTime start, FrameTime end) { QueuedEvent::execute(nframes, start, end); if (m_error == NO_ERROR) m_typed_event->execute(nframes, start, end); } void DisconnectionEvent::post_process() { if (m_error == NO_ERROR) { m_typed_event->post_process(); } else { // FIXME: better error messages string msg = "Unable to disconnect "; msg.append(m_src_port_path + " -> " + m_dst_port_path); _responder->respond_error(msg); } } //// TypedDisconnectionEvent //// template TypedDisconnectionEvent::TypedDisconnectionEvent(Engine& engine, CountedPtr responder, SampleCount timestamp, OutputPort* src_port, InputPort* dst_port) : QueuedEvent(engine, responder, timestamp), m_src_port(src_port), m_dst_port(dst_port), m_patch(NULL), m_process_order(NULL), m_succeeded(true) { assert(src_port != NULL); assert(dst_port != NULL); } template void TypedDisconnectionEvent::pre_process() { if (!m_dst_port->is_connected_to(m_src_port)) { m_succeeded = false; QueuedEvent::pre_process(); return; } Node* const src_node = m_src_port->parent_node(); Node* const dst_node = m_dst_port->parent_node(); // Connection to a patch port from inside the patch if (src_node->parent_patch() != dst_node->parent_patch()) { assert(src_node->parent() == dst_node || dst_node->parent() == src_node); if (src_node->parent() == dst_node) m_patch = dynamic_cast(dst_node); else m_patch = dynamic_cast(src_node); // Connection from a patch input to a patch output (pass through) } else if (src_node == dst_node && dynamic_cast(src_node)) { m_patch = dynamic_cast(src_node); // Normal connection between nodes with the same parent } else { m_patch = src_node->parent_patch(); } assert(m_patch); if (src_node == NULL || dst_node == NULL) { m_succeeded = false; QueuedEvent::pre_process(); return; } for (List::iterator i = dst_node->providers()->begin(); i != dst_node->providers()->end(); ++i) if ((*i) == src_node) { delete dst_node->providers()->remove(i); break; } for (List::iterator i = src_node->dependants()->begin(); i != src_node->dependants()->end(); ++i) if ((*i) == dst_node) { delete src_node->dependants()->remove(i); break; } if (m_patch->enabled()) m_process_order = m_patch->build_process_order(); m_succeeded = true; QueuedEvent::pre_process(); } template void TypedDisconnectionEvent::execute(SampleCount nframes, FrameTime start, FrameTime end) { QueuedEvent::execute(nframes, start, end); if (m_succeeded) { ListNode*>* const port_connection = m_dst_port->remove_connection(m_src_port); if (port_connection != NULL) { ListNode* const patch_connection = m_patch->remove_connection(m_src_port, m_dst_port); assert(patch_connection); assert((Connection*)port_connection->elem() == patch_connection->elem()); // Clean up both the list node and the connection itself... _engine.maid()->push(port_connection); _engine.maid()->push(patch_connection); _engine.maid()->push(port_connection->elem()); if (m_patch->process_order() != NULL) _engine.maid()->push(m_patch->process_order()); m_patch->process_order(m_process_order); } else { m_succeeded = false; // Ports weren't connected } } } template void TypedDisconnectionEvent::post_process() { if (m_succeeded) { _responder->respond_ok(); _engine.broadcaster()->send_disconnection(m_src_port->path(), m_dst_port->path()); } else { _responder->respond_error("Unable to disconnect ports."); } } } // namespace Ingen