/* This file is part of Om. Copyright (C) 2006 Dave Robillard. * * Om 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. * * Om 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 "PatchController.h" #include #include #include "GladeFactory.h" #include "Configuration.h" #include "util/Path.h" #include "ControlPanel.h" #include "ConnectionModel.h" #include "OmFlowCanvas.h" #include "PatchView.h" #include "flowcanvas/Module.h" #include "PluginModel.h" #include "Controller.h" #include "SubpatchModule.h" #include "DSSIModule.h" #include "PatchWindow.h" #include "NodeModel.h" #include "OmModule.h" #include "OmPortModule.h" #include "OmPort.h" #include "ControlModel.h" #include "NodeControlWindow.h" #include "NodeController.h" #include "PortController.h" #include "App.h" #include "PatchTreeWindow.h" #include "PatchPropertiesWindow.h" #include "DSSIController.h" #include "PatchModel.h" #include "Store.h" using std::cerr; using std::cout; using std::endl; using Om::Path; using namespace LibOmClient; namespace OmGtk { PatchController::PatchController(CountedPtr model) : NodeController(model), m_properties_window(NULL), m_window(NULL), m_patch_view(NULL), m_patch_model(model), m_module_x(0), m_module_y(0) { assert(model->path().length() > 0); assert(model->controller() == this); // NodeController() does this assert(m_patch_model == model); /* FIXME if (model->path() != "/") { PatchController* parent = Store::instance().patch(model->path().parent()); if (parent != NULL) parent->add_subpatch(this); else cerr << "[PatchController] " << path() << " ERROR: Parent not found." << endl; }*/ //model->new_port_sig.connect(sigc::mem_fun(this, &PatchController::add_port)); model->new_node_sig.connect(sigc::mem_fun(this, &PatchController::add_node)); model->removed_node_sig.connect(sigc::mem_fun(this, &PatchController::remove_node)); model->new_connection_sig.connect(sigc::mem_fun(this, &PatchController::connection)); model->removed_connection_sig.connect(sigc::mem_fun(this, &PatchController::disconnection)); } PatchController::~PatchController() { if (m_patch_view != NULL) { claim_patch_view(); m_patch_view->hide(); delete m_patch_view; m_patch_view = NULL; } if (m_control_window != NULL) { m_control_window->hide(); delete m_control_window; m_control_window = NULL; } if (m_window != NULL) { m_window->hide(); delete m_window; m_window = NULL; } } /* void PatchController::add_to_store() { Store::instance().add_object(this); } void PatchController::remove_from_store() { Store::instance().remove_object(this); } */ void PatchController::clear() { // Destroy model // Destroying nodes removes models from patch model, which invalidates any // iterator to nodes, so avoid the iterator problem by doing it this way: const NodeModelMap& nodes = patch_model()->nodes(); size_t remaining = nodes.size(); while (remaining > 0) { NodeController* const nc = (NodeController*)(*nodes.begin()).second->controller(); assert(nc != NULL); nc->destroy(); assert(nodes.size() == remaining - 1); --remaining; } assert(nodes.empty()); patch_model()->clear(); if (m_patch_view != NULL) { assert(m_patch_view->canvas() != NULL); m_patch_view->canvas()->destroy(); } } void PatchController::destroy() { // Destroying nodes removes models from patch model, which invalidates any // iterator to nodes, so avoid the iterator problem by doing it this way: const NodeModelMap& nodes = patch_model()->nodes(); size_t remaining = nodes.size(); while (remaining > 0) { NodeController* const nc = (NodeController*) (*nodes.begin()).second->controller(); assert(nc != NULL); nc->destroy(); assert(nodes.size() == remaining - 1); --remaining; } assert(nodes.empty()); //App::instance().remove_patch(this); App::instance().patch_tree()->remove_patch(path()); // Delete all children models //patch_model()->clear(); // Remove self from object store //Store::instance().remove_object(this); // Delete self from parent (this will delete model) /*if (patch_model()->parent() != NULL) { PatchController* const parent = (PatchController*)patch_model()->parent()->controller(); assert(parent != NULL); parent->remove_node(name()); } else { //delete m_model; }*/ } void PatchController::metadata_update(const string& key, const string& value) { NodeController::metadata_update(key, value); if (key == "filename") patch_model()->filename(value); } void PatchController::set_path(const Path& new_path) { assert(m_model); Path old_path = path(); // Rename nodes for (NodeModelMap::const_iterator i = patch_model()->nodes().begin(); i != patch_model()->nodes().end(); ++i) { const NodeModel* const nm = (*i).second.get(); assert(nm != NULL); NodeController* const nc = ((NodeController*)nm->controller()); assert(nc != NULL); nc->set_path(new_path.base_path() + nc->node_model()->name()); } #ifdef DEBUG // Be sure ports were renamed by their bridge nodes for (PortModelList::const_iterator i = node_model()->ports().begin(); i != node_model()->ports().end(); ++i) { GtkObjectController* const pc = (GtkObjectController*)((*i)->controller()); assert(pc != NULL); assert(pc->path().parent()== new_path); } #endif App::instance().patch_tree()->patch_renamed(old_path, new_path); if (m_window != NULL) m_window->patch_renamed(new_path); if (m_control_window != NULL) m_control_window->set_title(new_path + " Controls"); if (m_module != NULL) { assert(m_module->canvas() != NULL); m_module->canvas()->rename_module(old_path.name(), new_path.name()); assert(m_module->name() == new_path.name()); } PatchController* parent = dynamic_cast( patch_model()->parent()->controller()); if (parent != NULL && parent->window() != NULL) parent->window()->node_renamed(old_path, new_path); //remove_from_store(); GtkObjectController::set_path(new_path); //add_to_store(); if (old_path.name() != new_path.name()) parent->patch_model()->rename_node(old_path, new_path); } void PatchController::enable() { if (m_patch_view != NULL) m_patch_view->enabled(true); patch_model()->enabled(true); App::instance().patch_tree()->patch_enabled(m_model->path()); } void PatchController::disable() { if (m_patch_view != NULL) m_patch_view->enabled(false); patch_model()->enabled(false); App::instance().patch_tree()->patch_disabled(m_model->path()); } void PatchController::create_module(OmFlowCanvas* canvas) { //cerr << "Creating patch module " << m_model->path() << endl; assert(canvas != NULL); assert(m_module == NULL); assert(!m_patch_view || canvas != m_patch_view->canvas()); m_module = new SubpatchModule(canvas, this); m_menu.remove(m_menu.items()[4]); // Add navigation menu items Gtk::Menu::MenuList& items = m_menu.items(); items.push_front(Gtk::Menu_Helpers::SeparatorElem()); items.push_front(Gtk::Menu_Helpers::MenuElem("Browse to Patch", sigc::mem_fun((SubpatchModule*)m_module, &SubpatchModule::browse_to_patch))); items.push_front(Gtk::Menu_Helpers::MenuElem("Open Patch in New Window", sigc::mem_fun(this, &PatchController::show_patch_window))); create_all_ports(); m_module->move_to(node_model()->x(), node_model()->y()); } void PatchController::create_view() { assert(m_patch_view == NULL); Glib::RefPtr xml = GladeFactory::new_glade_reference(); xml->get_widget_derived("patch_view_vbox", m_patch_view); assert(m_patch_view != NULL); m_patch_view->patch_controller(this); assert(m_patch_view->canvas() != NULL); // Create modules for nodes for (NodeModelMap::const_iterator i = patch_model()->nodes().begin(); i != patch_model()->nodes().end(); ++i) { NodeModel* const nm = (*i).second.get(); string val = nm->get_metadata("module-x"); if (val != "") nm->x(atof(val.c_str())); val = nm->get_metadata("module-y"); if (val != "") nm->y(atof(val.c_str())); /* Set sane default coordinates if not set already yet */ if (nm->x() == 0.0f && nm->y() == 0.0f) { int x, y; get_new_module_location(x, y); nm->x(x); nm->y(y); } NodeController* nc = ((NodeController*)nm->controller()); if (!nc) nc = create_controller_for_node(nm); assert(nc); assert(nm->controller() == nc); if (nc->module() == NULL); nc->create_module(m_patch_view->canvas()); assert(nc->module() != NULL); m_patch_view->canvas()->add_module(nc->module()); } // Create pseudo modules for ports (ports on this canvas, not on our module) for (PortModelList::const_iterator i = patch_model()->ports().begin(); i != patch_model()->ports().end(); ++i) { PortController* const pc = dynamic_cast((*i)->controller()); assert(pc); if (pc->module() == NULL) pc->create_module(m_patch_view->canvas(), 1600, 1200); assert(pc->module() != NULL); m_patch_view->canvas()->add_module(pc->module()); pc->module()->resize(); } // Create connections for (list >::const_iterator i = patch_model()->connections().begin(); i != patch_model()->connections().end(); ++i) { create_connection(*i); } // Set run checkbox m_patch_view->enabled(patch_model()->enabled()); } void PatchController::show_properties_window() { if (!m_properties_window) { Glib::RefPtr glade_xml = GladeFactory::new_glade_reference(); glade_xml->get_widget_derived("patch_properties_win", m_properties_window); m_properties_window->patch_model(patch_model()); } m_properties_window->show(); } /** Create a connection in the view (canvas). */ void PatchController::create_connection(CountedPtr cm) { // Deal with messy anonymous nodes for this patch's own ports... const Path& src_parent_path = cm->src_port_path().parent(); const Path& dst_parent_path = cm->dst_port_path().parent(); const string& src_parent_name = (src_parent_path == path()) ? "" : src_parent_path.name(); const string& dst_parent_name = (dst_parent_path == path()) ? "" : dst_parent_path.name(); m_patch_view->canvas()->add_connection( src_parent_name, cm->src_port_path().name(), dst_parent_name, cm->dst_port_path().name()); // Disable control slider from destination node control window cerr << "FIXME: create_connection\n"; /*PortController* p = Store::instance().port(cm->dst_port_path()); assert(p != NULL); if (p->control_panel() != NULL) p->control_panel()->disable_port(p->path());*/ // FIXME: don't use canvas as a model (search object store) /*OmModule* m = (OmModule*)m_patch_view->canvas()->find_module( cm->dst_port_path().parent().name()); if (m != NULL) { OmPort* p = m->port(cm->dst_port_path().name()); if (p != NULL && p->connections().size() == 1) { p->model()->connected(true); assert(m->node_model()->controller() != NULL); NodeControlWindow* cw = (((NodeController*) m->node_model()->controller())->control_window()); if (cw != NULL) cw->control_panel()->disable_port(cm->dst_port_path()); } }*/ } NodeController* PatchController::create_controller_for_node(CountedPtr node) { assert(!node->controller()); NodeController* nc = NULL; CountedPtr patch(node); if (patch) { assert(patch == node); assert(patch->parent() == m_patch_model); nc = new PatchController(patch); } else { assert(node->plugin()); if (node->plugin()->type() == PluginModel::DSSI) nc = new DSSIController(node); else nc = new NodeController(node); } assert(node->controller() == nc); return nc; } /** Add a child node to this patch. * * This is for plugin nodes and patches, and is responsible for creating the * GtkObjectController for @a object (and through that the View if necessary) */ void PatchController::add_node(CountedPtr object) { assert(object); assert(object->parent() == m_patch_model); assert(patch_model()->get_node(object->name())); /*if (patch_model()->get_node(nm->name()) != NULL) { cerr << "Ignoring existing\n"; // Node already exists, ignore //delete nm; } else {*/ CountedPtr node(object); assert(node == object); if (node) { assert(node->parent() == m_patch_model); NodeController* nc = create_controller_for_node(node); assert(nc); assert(node->controller() == nc); if (m_patch_view != NULL) { int x, y; get_new_module_location(x, y); node->x(x); node->y(y); // Set zoom to 1.0 so module isn't messed up (Death to GnomeCanvas) float old_zoom = m_patch_view->canvas()->zoom(); if (old_zoom != 1.0) m_patch_view->canvas()->zoom(1.0); if (nc->module() == NULL) nc->create_module(m_patch_view->canvas()); assert(nc->module() != NULL); m_patch_view->canvas()->add_module(nc->module()); nc->module()->resize(); // Reset zoom if (old_zoom != 1.0) { m_patch_view->canvas()->zoom(old_zoom); nc->module()->zoom(old_zoom); } } } } /** Removes a node from this patch. */ void PatchController::remove_node(const string& name) { assert(name.find("/") == string::npos); assert(!m_patch_model->get_node(name)); // Update breadcrumbs if necessary if (m_window != NULL) m_window->node_removed(name); if (m_patch_view != NULL) { assert(m_patch_view->canvas() != NULL); m_patch_view->canvas()->remove_module(name); } } /** Add a port to this patch. * * Will add a port to the subpatch module and the control window, if they * exist. */ void PatchController::add_port(CountedPtr pm) { assert(pm); assert(pm->parent() == m_patch_model); assert(patch_model()->get_port(pm->name())); cerr << "ADDING PORT " << pm->name() << "TO PATCH: " << patch_model()->path() << endl; //cerr << "[PatchController] Adding port " << pm->path() << endl; /*if (patch_model()->get_port(pm->name())) { cerr << "[PatchController] Ignoring duplicate port " << pm->path() << endl; return; }*/ //node_model()->add_port(pm); // FIXME: leak PortController* pc = new PortController(pm); // Handle bridge ports/nodes (this is uglier than it should be) /*NodeController* nc = (NodeController*)Store::instance().node(pm->path())->controller(); if (nc != NULL) nc->bridge_port(pc); */ // Create port on this patch's module (if there is one) if (m_module != NULL) { pc->create_port(m_module); m_module->resize(); } // Create port's (pseudo) module on this patch's canvas (if there is one) if (m_patch_view != NULL) { int x, y; get_new_module_location(x, y); // Set zoom to 1.0 so module isn't messed up (Death to GnomeCanvas) float old_zoom = m_patch_view->canvas()->zoom(); if (old_zoom != 1.0) m_patch_view->canvas()->zoom(1.0); if (pc->module() == NULL) pc->create_module(m_patch_view->canvas(), x, y); assert(pc->module() != NULL); m_patch_view->canvas()->add_module(pc->module()); pc->module()->resize(); // Reset zoom if (old_zoom != 1.0) { m_patch_view->canvas()->zoom(old_zoom); pc->module()->zoom(old_zoom); } } if (m_control_window != NULL) { assert(m_control_window->control_panel() != NULL); m_control_window->control_panel()->add_port(pc); m_control_window->resize(); } // Enable "Controls" menuitem on module and patch window, if necessary if (has_control_inputs()) enable_controls_menuitem(); } /** Removes a port from this patch */ void PatchController::remove_port(const Path& path, bool resize_module) { assert(path.parent() == m_model->path()); //cerr << "[PatchController] Removing port " << path << endl; /* FIXME if (m_control_panel != NULL) { m_control_panel->remove_port(path); if (m_control_window != NULL) { assert(m_control_window->control_panel() == m_control_panel); m_control_window->resize(); } }*/ // Remove port on module if (m_module != NULL) { assert(m_module->port(path.name()) != NULL); m_module->remove_port(path.name(), resize_module); assert(m_module->port(path.name()) == NULL); } patch_model()->remove_port(path); assert(patch_model()->get_port(path.name())); // Disable "Controls" menuitem on module and patch window, if necessary if (!has_control_inputs()) disable_controls_menuitem(); } void PatchController::connection(CountedPtr cm) { assert(cm); //patch_model()->add_connection(cm); if (m_patch_view != NULL) create_connection(cm); } void PatchController::disconnection(const Path& src_port_path, const Path& dst_port_path) { const string& src_node_name = src_port_path.parent().name(); const string& src_port_name = src_port_path.name(); const string& dst_node_name = dst_port_path.parent().name(); const string& dst_port_name = dst_port_path.name(); if (m_patch_view != NULL) m_patch_view->canvas()->remove_connection( src_node_name, src_port_name, dst_node_name, dst_port_name); //patch_model()->remove_connection(src_port_path, dst_port_path); cerr << "FIXME: disconnection\n"; /* // Enable control slider in destination node control window PortController* p = (PortController)Store::instance().port(dst_port_path)->controller(); assert(p != NULL); if (p->control_panel() != NULL) p->control_panel()->enable_port(p->path()); */ } /** Try to guess a suitable location for a new module. */ void PatchController::get_new_module_location(int& x, int& y) { assert(m_patch_view != NULL); assert(m_patch_view->canvas() != NULL); m_patch_view->canvas()->get_scroll_offsets(x, y); x += 20; y += 20; } void PatchController::show_patch_window() { if (m_window == NULL) { Glib::RefPtr xml = GladeFactory::new_glade_reference(); xml->get_widget_derived("patch_win", m_window); assert(m_window != NULL); if (m_patch_view == NULL) create_view(); m_window->patch_controller(this); } assert(m_window != NULL); m_window->present(); } /** Become the parent of the patch view. * * Steals the view away from whatever window is currently showing it. */ void PatchController::claim_patch_view() { assert(m_patch_view != NULL); m_patch_view->hide(); m_patch_view->reparent(m_patch_view_bin); } void PatchController::show_control_window() { assert(patch_model()); if (m_control_window == NULL) m_control_window = new NodeControlWindow(this, patch_model()->poly()); if (m_control_window->control_panel()->num_controls() > 0) m_control_window->present(); } void PatchController::enable_controls_menuitem() { if (m_window != NULL) m_window->menu_view_control_window()->property_sensitive() = true; NodeController::enable_controls_menuitem(); } void PatchController::disable_controls_menuitem() { if (m_window != NULL) m_window->menu_view_control_window()->property_sensitive() = false; NodeController::disable_controls_menuitem(); } } // namespace OmGtk