/* This file is part of Machina. * Copyright (C) 2007-2009 David Robillard * * Machina 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 3 of the License, or * (at your option) any later version. * * Machina 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 more details. * * You should have received a copy of the GNU General Public License * along with Machina. If not, see . */ #include #include "raul/SharedPtr.hpp" #include "raul/TimeStamp.hpp" #include "machina/Action.hpp" #include "machina/Edge.hpp" #include "machina/Engine.hpp" #include "machina/Machine.hpp" #include "machina/Node.hpp" #include "EdgeView.hpp" #include "MachinaCanvas.hpp" #include "MachinaGUI.hpp" #include "NodeView.hpp" using namespace Raul; using namespace FlowCanvas; MachinaCanvas::MachinaCanvas(MachinaGUI* app, int width, int height) : Canvas(width, height) , _app(app) , _selector_dash(NULL) { grab_focus(); } void MachinaCanvas::node_clicked(WeakPtr item, GdkEventButton* event) { SharedPtr node = PtrCast(item.lock()); if (!node) return; if (event->state & GDK_CONTROL_MASK) return; // Middle click, learn if (event->button == 2) { _app->engine()->machine()->learn(_app->maid(), node->node()); return; } else if (event->button == 3) { SharedPtr last = _last_clicked.lock(); if (last) { if (node != last) { if (get_connection(last, node)) disconnect_node(last, node); else connect_node(last, node); } last->set_default_base_color(); _last_clicked.reset(); } else { _last_clicked = node; node->set_base_color(0xFF0000FF); } } } bool MachinaCanvas::canvas_event(GdkEvent* event) { static int last = 0; SharedPtr machine = _app->engine()->machine(); if (!machine) return false; if (event->type == GDK_BUTTON_RELEASE && event->button.button == 3 && !(event->button.state & (GDK_CONTROL_MASK))) { const double x = event->button.x; const double y = event->button.y; string name = string("Note")+(char)(last++ +'0'); TimeDuration dur(machine->time().unit(), 1.0); SharedPtr node(new Machina::Node(dur, false)); SharedPtr view(new NodeView(_app->window(), shared_from_this(), node, name, x, y)); view->signal_clicked.connect(sigc::bind<0>(sigc::mem_fun(this, &MachinaCanvas::node_clicked), WeakPtr(view))); add_item(view); view->resize(); view->raise_to_top(); machine->add_node(node); return true; } else { return Canvas::canvas_event(event); } } void MachinaCanvas::connect_node(boost::shared_ptr src, boost::shared_ptr head) { SharedPtr edge(new Machina::Edge(src->node(), head->node())); src->node()->add_edge(edge); boost::shared_ptr c(new EdgeView(shared_from_this(), src, head, edge)); src->add_connection(c); head->add_connection(c); add_connection(c); } void MachinaCanvas::disconnect_node(boost::shared_ptr src, boost::shared_ptr head) { src->node()->remove_edges_to(head->node()); remove_connection(src, head); } SharedPtr MachinaCanvas::create_node_view(SharedPtr node) { SharedPtr view(new NodeView(_app->window(), shared_from_this(), node, "", 10, 10)); if ( ! node->enter_action() && ! node->exit_action() ) view->set_base_color(0x101010FF); view->signal_clicked.connect(sigc::bind<0>(sigc::mem_fun(this, &MachinaCanvas::node_clicked), WeakPtr(view))); add_item(view); return view; } void MachinaCanvas::build(SharedPtr machine, bool show_labels) { destroy(); _last_clicked.reset(); assert(_items.empty()); if (!machine) return; std::map, SharedPtr > views; for (Machina::Machine::Nodes::const_iterator i = machine->nodes().begin(); i != machine->nodes().end(); ++i) { const SharedPtr view = create_node_view(*i); views.insert(std::make_pair((*i), view)); } for (ItemList::iterator i = _items.begin(); i != _items.end(); ++i) { const SharedPtr view = PtrCast(*i); if (!view) continue; for (Machina::Node::Edges::const_iterator e = view->node()->edges().begin(); e != view->node()->edges().end(); ++e) { SharedPtr head_view = views[(*e)->head()]; if (!head_view) { cerr << "WARNING: Edge to node with no view" << endl; continue; } boost::shared_ptr c(new EdgeView(shared_from_this(), view, head_view, (*e))); view->add_connection(c); head_view->add_connection(c); add_connection(c); } } arrange(); } void MachinaCanvas::update_edges() { for (ConnectionList::iterator i = _connections.begin(); i != _connections.end(); ++i) { SharedPtr edge = PtrCast(*i); if (edge) edge->update(); } }