diff options
-rw-r--r-- | src/engine/LearnRequest.cpp | 4 | ||||
-rw-r--r-- | src/engine/Loader.cpp | 4 | ||||
-rw-r--r-- | src/engine/Machine.cpp | 8 | ||||
-rw-r--r-- | src/engine/Node.cpp | 16 | ||||
-rw-r--r-- | src/engine/SMFDriver.cpp | 136 | ||||
-rw-r--r-- | src/engine/machina/Machine.hpp | 1 | ||||
-rw-r--r-- | src/engine/machina/Node.hpp | 14 | ||||
-rw-r--r-- | src/engine/machina/SMFDriver.hpp | 7 | ||||
-rw-r--r-- | src/gui/MachinaCanvas.cpp | 4 | ||||
-rw-r--r-- | src/gui/MachinaGUI.cpp | 27 | ||||
-rw-r--r-- | src/gui/Makefile.am | 5 | ||||
-rw-r--r-- | src/gui/NodePropertiesWindow.cpp | 99 | ||||
-rw-r--r-- | src/gui/NodePropertiesWindow.hpp | 51 | ||||
-rw-r--r-- | src/gui/NodeView.cpp | 22 | ||||
-rw-r--r-- | src/gui/NodeView.hpp | 5 | ||||
-rw-r--r-- | src/gui/machina.glade | 181 | ||||
-rw-r--r-- | src/gui/main.cpp | 14 |
17 files changed, 522 insertions, 76 deletions
diff --git a/src/engine/LearnRequest.cpp b/src/engine/LearnRequest.cpp index 6f311cb..019f7a2 100644 --- a/src/engine/LearnRequest.cpp +++ b/src/engine/LearnRequest.cpp @@ -25,8 +25,8 @@ namespace Machina { void LearnRequest::finish(BeatTime time) { - _node->add_enter_action(_enter_action); - _node->add_exit_action(_exit_action); + _node->set_enter_action(_enter_action); + _node->set_exit_action(_exit_action); double duration = Raul::Quantizer::quantize(_quantization, time - _start_time); diff --git a/src/engine/Loader.cpp b/src/engine/Loader.cpp index b58d150..02aff51 100644 --- a/src/engine/Loader.cpp +++ b/src/engine/Loader.cpp @@ -157,8 +157,8 @@ Loader::load(const Glib::ustring& uri) SharedPtr<Node> node = node_i->second; const long note_num = strtol(note.c_str(), NULL, 10); if (note_num >= 0 && note_num <= 127) { - node->add_enter_action(ActionFactory::note_on((unsigned char)note_num)); - node->add_exit_action(ActionFactory::note_off((unsigned char)note_num)); + node->set_enter_action(ActionFactory::note_on((unsigned char)note_num)); + node->set_exit_action(ActionFactory::note_off((unsigned char)note_num)); } else { cerr << "WARNING: MIDI note number out of range, ignoring." << endl; } diff --git a/src/engine/Machine.cpp b/src/engine/Machine.cpp index f2aa845..91dcfe4 100644 --- a/src/engine/Machine.cpp +++ b/src/engine/Machine.cpp @@ -55,10 +55,18 @@ Machine::set_sink(SharedPtr<Raul::MIDISink> sink) void Machine::add_node(SharedPtr<Node> node) { + cerr << "ADDING NODE " << node.get() << endl; _nodes.push_back(node); } +void +Machine::remove_node(SharedPtr<Node> node) +{ + _nodes.erase(_nodes.find(node)); +} + + /** Exit all active states and reset time to 0. */ void diff --git a/src/engine/Node.cpp b/src/engine/Node.cpp index 4a1ded0..d94e0bc 100644 --- a/src/engine/Node.cpp +++ b/src/engine/Node.cpp @@ -33,14 +33,14 @@ Node::Node(BeatCount duration, bool initial) void -Node::add_enter_action(SharedPtr<Action> action) +Node::set_enter_action(SharedPtr<Action> action) { _enter_action = action; } void -Node::remove_enter_action(SharedPtr<Action> /*action*/) +Node::remove_enter_action() { _enter_action.reset(); } @@ -48,14 +48,14 @@ Node::remove_enter_action(SharedPtr<Action> /*action*/) void -Node::add_exit_action(SharedPtr<Action> action) +Node::set_exit_action(SharedPtr<Action> action) { _exit_action = action; } void -Node::remove_exit_action(SharedPtr<Action> /*action*/) +Node::remove_exit_action() { _exit_action.reset(); } @@ -65,7 +65,9 @@ Node::remove_exit_action(SharedPtr<Action> /*action*/) void Node::enter(SharedPtr<Raul::MIDISink> sink, BeatTime time) { - //cerr << "ENTER " << time << endl; + assert(!_is_active); + //std::cerr << "ENTER " << time << std::endl; + _is_active = true; _enter_time = time; if (sink && _enter_action) @@ -76,7 +78,9 @@ Node::enter(SharedPtr<Raul::MIDISink> sink, BeatTime time) void Node::exit(SharedPtr<Raul::MIDISink> sink, BeatTime time) { - //cerr << "EXIT " << time << endl; + assert(_is_active); + //std::cerr << "EXIT " << time << std::endl; + if (sink && _exit_action) _exit_action->execute(sink, time); _is_active = false; diff --git a/src/engine/SMFDriver.cpp b/src/engine/SMFDriver.cpp index e14e04c..ff55e00 100644 --- a/src/engine/SMFDriver.cpp +++ b/src/engine/SMFDriver.cpp @@ -96,6 +96,54 @@ SMFDriver::learn(const string& filename, double q, Raul::BeatTime max_duration) } +bool +SMFDriver::is_delay_node(SharedPtr<Node> node) const +{ + if (node->enter_action() || node->exit_action()) + return false; + else + return true; +} + + +/** Connect two nodes, inserting a delay node between them if necessary. + * + * If a delay node is added to the machine, it is returned. + */ +SharedPtr<Node> +SMFDriver::connect_nodes(SharedPtr<Machine> m, + SharedPtr<Node> tail, Raul::BeatTime tail_end_time, + SharedPtr<Node> head, Raul::BeatTime head_start_time) +{ + assert(tail != head); + assert(head_start_time >= tail_end_time); + + SharedPtr<Node> delay_node; + + cout << "Connect nodes durations: " << tail->duration() << " .. " << head->duration() << endl; + cout << "Connect nodes times: " << tail_end_time << " .. " << head_start_time << endl; + + if (head_start_time == tail_end_time) { + // Connect directly + tail->add_outgoing_edge(SharedPtr<Edge>(new Edge(tail, head))); + } else if (is_delay_node(tail)) { + // Tail is a delay node, just accumulate the time difference into it + tail->set_duration(tail->duration() + head_start_time - tail_end_time); + tail->add_outgoing_edge(SharedPtr<Edge>(new Edge(tail, head))); + } else { + // Need to actually create a delay node + cerr << "Adding delay node for " << tail_end_time << " .. " << head_start_time << endl; + delay_node = SharedPtr<Node>(new Node()); + delay_node->set_duration(head_start_time - tail_end_time); + tail->add_outgoing_edge(SharedPtr<Edge>(new Edge(tail, delay_node))); + delay_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(delay_node, head))); + m->add_node(delay_node); + } + + return delay_node; +} + + void SMFDriver::learn_track(SharedPtr<Machine> m, Raul::SMFReader& reader, @@ -107,7 +155,11 @@ SMFDriver::learn_track(SharedPtr<Machine> m, if (!found_track) return; - list<SharedPtr<Node> > active_nodes; + typedef list<SharedPtr<Node> > ActiveList; + ActiveList active_nodes; + + typedef list<pair<Raul::BeatTime, SharedPtr<Node> > > PolyList; + PolyList poly_nodes; SharedPtr<Node> initial_node(new Node()); initial_node->set_initial(true); @@ -135,28 +187,21 @@ SMFDriver::learn_track(SharedPtr<Machine> m, if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_ON) { //cerr << "NOTE ON: " << (int)buf[1] << ", channel = " << (int)(buf[0] & 0x0F) << endl; SharedPtr<Node> node(new Node()); - node->add_enter_action(SharedPtr<Action>(new MidiAction(ev_size, buf))); - assert(connect_node_end_time <= t); - if (t == connect_node_end_time) { - connect_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(connect_node, node))); - } else { - SharedPtr<Node> delay_node(new Node()); - delay_node->set_duration(t - connect_node_end_time); - connect_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(connect_node, delay_node))); - delay_node->add_outgoing_edge(SharedPtr<Edge>(new Edge(delay_node, node))); - m->add_node(delay_node); - ++added_nodes; + node->set_enter_action(SharedPtr<Action>(new MidiAction(ev_size, buf))); + + SharedPtr<Node> delay_node = connect_nodes(m, connect_node, connect_node_end_time, node, t); + if (delay_node) { connect_node = delay_node; connect_node_end_time = t; } node->enter(SharedPtr<Raul::MIDISink>(), t); active_nodes.push_back(node); + } else if ((buf[0] & 0xF0) == MIDI_CMD_NOTE_OFF) { //cerr << "NOTE OFF: " << (int)buf[1] << endl; - for (list<SharedPtr<Node> >::iterator i = active_nodes.begin(); - i != active_nodes.end(); ++i) { + for (ActiveList::iterator i = active_nodes.begin(); i != active_nodes.end(); ++i) { SharedPtr<MidiAction> action = PtrCast<MidiAction>((*i)->enter_action()); if (!action) continue; @@ -167,17 +212,64 @@ SMFDriver::learn_track(SharedPtr<Machine> m, && (ev[0] & 0x0F) == (buf[0] & 0x0F) // same channel && ev[1] == buf[1]) // same note { - //cerr << "FOUND MATCHING NOTE ON!\n"; - (*i)->add_exit_action(SharedPtr<Action>(new MidiAction(ev_size, buf))); - (*i)->set_duration(t - (*i)->enter_time()); - (*i)->exit(SharedPtr<Raul::MIDISink>(), t); - m->add_node((*i)); + //cerr << "FOUND MATCHING NOTE OFF!\n"; + + SharedPtr<Node> resolved = *i; + + resolved->set_exit_action(SharedPtr<Action>(new MidiAction(ev_size, buf))); + resolved->set_duration(t - resolved->enter_time()); + ++added_nodes; + + connect_node_end_time = t; + if (active_nodes.size() == 1) { - connect_node = (*i); - connect_node_end_time = t; + if (poly_nodes.size() > 0) { + + connect_node = SharedPtr<Node>(new Node()); + m->add_node(connect_node); + + connect_nodes(m, resolved, t, connect_node, t); + + for (PolyList::iterator j = poly_nodes.begin(); + j != poly_nodes.end(); ++j) { + m->add_node(j->second); + connect_nodes(m, j->second, j->first + j->second->duration(), + connect_node, t); + } + poly_nodes.clear(); + + m->add_node(resolved); + + } else { + // Trim useless delay node, if possible + // (these happen after polyphonic sections) + if (is_delay_node(connect_node) && connect_node->duration() == 0 + && connect_node->outgoing_edges().size() == 1 + && (*connect_node->outgoing_edges().begin())->dst() == resolved) { + connect_node->outgoing_edges().clear(); + assert(connect_node->outgoing_edges().empty()); + connect_node->set_enter_action(resolved->enter_action()); + connect_node->set_exit_action(resolved->exit_action()); + resolved->remove_enter_action(); + resolved->remove_exit_action(); + connect_node->set_duration(resolved->duration()); + resolved = connect_node; + } else { + connect_node = resolved; + m->add_node(resolved); + } + } + + } else { + poly_nodes.push_back(make_pair(resolved->enter_time(), resolved)); } + + if (resolved->is_active()) + resolved->exit(SharedPtr<Raul::MIDISink>(), t); + active_nodes.erase(i); + break; } } @@ -197,7 +289,7 @@ SMFDriver::learn_track(SharedPtr<Machine> m, const unsigned char* ev = action->event(); if (ev_size == 3 && (ev[0] & 0xF0) == MIDI_CMD_NOTE_ON) { unsigned char note_off[3] = { ((MIDI_CMD_NOTE_OFF & 0xF0) | (ev[0] & 0x0F)), ev[1], 0x40 }; - (*i)->add_exit_action(SharedPtr<Action>(new MidiAction(3, note_off))); + (*i)->set_exit_action(SharedPtr<Action>(new MidiAction(3, note_off))); (*i)->set_duration(t - (*i)->enter_time()); (*i)->exit(SharedPtr<Raul::MIDISink>(), t); m->add_node((*i)); diff --git a/src/engine/machina/Machine.hpp b/src/engine/machina/Machine.hpp index b8e371d..e5bef23 100644 --- a/src/engine/machina/Machine.hpp +++ b/src/engine/machina/Machine.hpp @@ -43,6 +43,7 @@ public: bool is_finished() { return _is_finished; } void add_node(SharedPtr<Node> node); + void remove_node(SharedPtr<Node> node); void learn(SharedPtr<LearnRequest> learn); void write_state(Raul::RDFWriter& writer); diff --git a/src/engine/machina/Node.hpp b/src/engine/machina/Node.hpp index 2f9c2e4..5181149 100644 --- a/src/engine/machina/Node.hpp +++ b/src/engine/machina/Node.hpp @@ -46,11 +46,11 @@ public: Node(BeatCount duration=0, bool initial=false); - void add_enter_action(SharedPtr<Action> action); - void remove_enter_action(SharedPtr<Action> action); + void set_enter_action(SharedPtr<Action> action); + void remove_enter_action(); - void add_exit_action(SharedPtr<Action> action); - void remove_exit_action(SharedPtr<Action> action); + void set_exit_action(SharedPtr<Action> action); + void remove_exit_action(); SharedPtr<Action> enter_action() { return _enter_action; } SharedPtr<Action> exit_action() { return _exit_action; } @@ -67,13 +67,13 @@ public: bool is_initial() const { return _is_initial; } void set_initial(bool i) { _is_initial = i; } bool is_active() const { return _is_active; } - BeatTime enter_time() const { return _enter_time; } - BeatTime exit_time() const { return _enter_time + _duration; } + BeatTime enter_time() const { assert(_is_active); return _enter_time; } + BeatTime exit_time() const { assert(_is_active); return _enter_time + _duration; } BeatCount duration() { return _duration; } void set_duration(BeatCount d) { _duration = d; } typedef Raul::List<SharedPtr<Edge> > Edges; - const Edges& outgoing_edges() const { return _outgoing_edges; } + Edges& outgoing_edges() { return _outgoing_edges; } private: bool _is_initial; diff --git a/src/engine/machina/SMFDriver.hpp b/src/engine/machina/SMFDriver.hpp index 090eb02..7fec256 100644 --- a/src/engine/machina/SMFDriver.hpp +++ b/src/engine/machina/SMFDriver.hpp @@ -55,6 +55,13 @@ public: private: SharedPtr<Raul::SMFWriter> _writer; + bool is_delay_node(SharedPtr<Node> node) const; + + SharedPtr<Node> + connect_nodes(SharedPtr<Machine> m, + SharedPtr<Node> tail, Raul::BeatTime tail_end_time, + SharedPtr<Node> head, Raul::BeatTime head_start_time); + void learn_track(SharedPtr<Machine> machine, Raul::SMFReader& reader, unsigned track, diff --git a/src/gui/MachinaCanvas.cpp b/src/gui/MachinaCanvas.cpp index 6fdbc86..e11532e 100644 --- a/src/gui/MachinaCanvas.cpp +++ b/src/gui/MachinaCanvas.cpp @@ -98,7 +98,7 @@ MachinaCanvas::canvas_event(GdkEvent* event) SharedPtr<Machina::Node> node(new Machina::Node(1.0, false)); //node->add_enter_action(SharedPtr<Machina::Action>(new Machina::PrintAction(name))); - SharedPtr<NodeView> view(new NodeView(node, shared_from_this(), + SharedPtr<NodeView> view(new NodeView(_app->window(), shared_from_this(), node, name, x, y)); view->signal_clicked.connect(sigc::bind<0>(sigc::mem_fun(this, @@ -173,7 +173,7 @@ MachinaCanvas::disconnect_node(boost::shared_ptr<NodeView> src, SharedPtr<NodeView> MachinaCanvas::create_node_view(SharedPtr<Machina::Node> node) { - SharedPtr<NodeView> view(new NodeView(node, shared_from_this(), + SharedPtr<NodeView> view(new NodeView(_app->window(), shared_from_this(), node, "", 10, 10)); if ( ! node->enter_action() && ! node->exit_action() ) diff --git a/src/gui/MachinaGUI.cpp b/src/gui/MachinaGUI.cpp index fdd75c6..381d8f4 100644 --- a/src/gui/MachinaGUI.cpp +++ b/src/gui/MachinaGUI.cpp @@ -24,6 +24,7 @@ #include <raul/RDFWriter.h> #include <machina/Machine.hpp> #include <machina/SMFDriver.hpp> +#include "GladeXml.hpp" #include "MachinaGUI.hpp" #include "MachinaCanvas.hpp" #include "NodeView.hpp" @@ -42,31 +43,7 @@ MachinaGUI::MachinaGUI(SharedPtr<Machina::Engine> engine) _canvas = boost::shared_ptr<MachinaCanvas>(new MachinaCanvas(this, 1600*2, 1200*2)); - Glib::RefPtr<Gnome::Glade::Xml> xml; - - // Check for the .glade file in current directory - string glade_filename = "./machina.glade"; - ifstream fs(glade_filename.c_str()); - if (fs.fail()) { // didn't find it, check PKGDATADIR - fs.clear(); - glade_filename = PKGDATADIR; - glade_filename += "/machina.glade"; - - fs.open(glade_filename.c_str()); - if (fs.fail()) { - cerr << "Unable to find machina.glade in current directory or " - << PKGDATADIR << "." << endl; - exit(EXIT_FAILURE); - } - fs.close(); - } - - try { - xml = Gnome::Glade::Xml::create(glade_filename); - } catch(const Gnome::Glade::XmlError& ex) { - std::cerr << ex.what() << std::endl; - throw; - } + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeXml::create(); xml->get_widget("machina_win", _main_window); xml->get_widget("about_win", _about_window); diff --git a/src/gui/Makefile.am b/src/gui/Makefile.am index 1d6390d..3a204f2 100644 --- a/src/gui/Makefile.am +++ b/src/gui/Makefile.am @@ -15,6 +15,7 @@ machina_gui_LDADD = @FLOWCANVAS_LIBS@ @LIBGLADEMM_LIBS@ @GNOMECANVASMM_LIBS@ @JA bin_PROGRAMS = machina_gui machina_gui_SOURCES = \ main.cpp \ + GladeXml.hpp \ MachinaGUI.hpp \ MachinaGUI.cpp \ MachinaCanvas.hpp \ @@ -22,6 +23,8 @@ machina_gui_SOURCES = \ NodeView.hpp \ NodeView.cpp \ EdgeView.hpp \ - EdgeView.cpp + EdgeView.cpp \ + NodePropertiesWindow.hpp \ + NodePropertiesWindow.cpp endif diff --git a/src/gui/NodePropertiesWindow.cpp b/src/gui/NodePropertiesWindow.cpp new file mode 100644 index 0000000..151c602 --- /dev/null +++ b/src/gui/NodePropertiesWindow.cpp @@ -0,0 +1,99 @@ +/* This file is part of Machina. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * 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 2 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 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 <string> +#include "machina/MidiAction.hpp" +#include "NodePropertiesWindow.hpp" +#include "GladeXml.hpp" + +using namespace std; +using namespace Machina; + + +NodePropertiesWindow* NodePropertiesWindow::_instance = NULL; + + +NodePropertiesWindow::NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& xml) + : Gtk::Dialog(cobject) +{ + property_visible() = false; + + xml->get_widget("node_properties_note_spinbutton", _note_spinbutton); + xml->get_widget("node_properties_duration_spinbutton", _duration_spinbutton); + xml->get_widget("node_properties_cancel_button", _cancel_button); + xml->get_widget("node_properties_ok_button", _ok_button); + + _ok_button->signal_clicked().connect(sigc::mem_fun(this, &NodePropertiesWindow::ok_clicked)); + _cancel_button->signal_clicked().connect(sigc::mem_fun(this, &NodePropertiesWindow::cancel_clicked)); +} + + +NodePropertiesWindow::~NodePropertiesWindow() +{ +} + + +void +NodePropertiesWindow::ok_clicked() +{ + assert(this == _instance); + delete _instance; + _instance = NULL; +} + + +void +NodePropertiesWindow::cancel_clicked() +{ + assert(this == _instance); + delete _instance; + _instance = NULL; +} + + +void +NodePropertiesWindow::set_node(SharedPtr<Machina::Node> node) +{ + _node = node; + SharedPtr<MidiAction> enter_action = PtrCast<MidiAction>(node->enter_action()); + if (enter_action && enter_action->event_size() > 1 + && (enter_action->event()[0] & 0xF0) == 0x90) { + _note_spinbutton->set_value(enter_action->event()[1]); + _note_spinbutton->show(); + } else { + _note_spinbutton->hide(); + } + _duration_spinbutton->set_value(node->duration()); +} + + +void +NodePropertiesWindow::present(Gtk::Window* parent, SharedPtr<Machina::Node> node) +{ + if (!_instance) { + Glib::RefPtr<Gnome::Glade::Xml> xml = GladeXml::create(); + + xml->get_widget_derived("node_properties_dialog", _instance); + + if (parent) + _instance->set_transient_for(*parent); + } + + _instance->set_node(node); + _instance->show(); +} + diff --git a/src/gui/NodePropertiesWindow.hpp b/src/gui/NodePropertiesWindow.hpp new file mode 100644 index 0000000..461b51b --- /dev/null +++ b/src/gui/NodePropertiesWindow.hpp @@ -0,0 +1,51 @@ +/* This file is part of Machina. + * Copyright (C) 2007 Dave Robillard <http://drobilla.net> + * + * 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 2 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 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 + */ + +#ifndef NODEPROPERTIESWINDOW_H +#define NODEPROPERTIESWINDOW_H + +#include <gtkmm.h> +#include <libglademm/xml.h> +#include "machina/Node.hpp" + +class NodePropertiesWindow : public Gtk::Dialog +{ +public: + static void present(Gtk::Window* parent, SharedPtr<Machina::Node> node); + +private: + friend class Gnome::Glade::Xml; + NodePropertiesWindow(BaseObjectType* cobject, const Glib::RefPtr<Gnome::Glade::Xml>& glade_xml); + ~NodePropertiesWindow(); + + void set_node(SharedPtr<Machina::Node> node); + + void ok_clicked(); + void cancel_clicked(); + + static NodePropertiesWindow* _instance; + + SharedPtr<Machina::Node> _node; + + Gtk::SpinButton* _note_spinbutton; + Gtk::SpinButton* _duration_spinbutton; + Gtk::Button* _cancel_button; + Gtk::Button* _ok_button; +}; + + +#endif // NODEPROPERTIESWINDOW_H diff --git a/src/gui/NodeView.cpp b/src/gui/NodeView.cpp index d675f1d..be40047 100644 --- a/src/gui/NodeView.cpp +++ b/src/gui/NodeView.cpp @@ -16,14 +16,17 @@ */ #include "NodeView.hpp" -#include <iostream> +#include "NodePropertiesWindow.hpp" -NodeView::NodeView(SharedPtr<Machina::Node> node, + +NodeView::NodeView(Gtk::Window* window, SharedPtr<LibFlowCanvas::FlowCanvas> canvas, + SharedPtr<Machina::Node> node, const std::string& name, double x, double y) : LibFlowCanvas::Ellipse(canvas, name, x, y, 20, 20, false) + , _window(window) , _node(node) { //signal_double_clicked.connect(sigc::mem_fun(this, &NodeView::on_double_click)); @@ -33,9 +36,18 @@ NodeView::NodeView(SharedPtr<Machina::Node> node, void NodeView::on_double_click(GdkEventButton*) { - bool is_initial = _node->is_initial(); - _node->set_initial( ! is_initial ); - set_border_width(is_initial ? 1.0 : 2.0); + NodePropertiesWindow::present(_window, _node); +} + + +void +NodeView::on_click(GdkEventButton* event) +{ + if (event->button == 3) { + bool is_initial = _node->is_initial(); + _node->set_initial( ! is_initial ); + set_border_width(is_initial ? 1.0 : 2.0); + } } diff --git a/src/gui/NodeView.hpp b/src/gui/NodeView.hpp index ae5c34e..4641787 100644 --- a/src/gui/NodeView.hpp +++ b/src/gui/NodeView.hpp @@ -24,8 +24,9 @@ class NodeView : public LibFlowCanvas::Ellipse { public: - NodeView(SharedPtr<Machina::Node> node, + NodeView(Gtk::Window* window, SharedPtr<LibFlowCanvas::FlowCanvas> canvas, + SharedPtr<Machina::Node> node, const std::string& name, double x, double y); @@ -35,8 +36,10 @@ public: void update_state(); private: + void on_click(GdkEventButton* ev); void on_double_click(GdkEventButton* ev); + Gtk::Window* _window; SharedPtr<Machina::Node> _node; uint32_t _old_color; }; diff --git a/src/gui/machina.glade b/src/gui/machina.glade index 8536e8f..df73a85 100644 --- a/src/gui/machina.glade +++ b/src/gui/machina.glade @@ -672,4 +672,185 @@ Connect some nodes up and double click one. You'll get it.</property> </child> </widget> +<widget class="GtkDialog" id="node_properties_dialog"> + <property name="border_width">8</property> + <property name="title" translatable="yes">Node Properties</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">False</property> + <property name="destroy_with_parent">True</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">True</property> + <property name="skip_pager_hint">True</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + <property name="focus_on_map">True</property> + <property name="urgency_hint">False</property> + <property name="has_separator">True</property> + + <child internal-child="vbox"> + <widget class="GtkVBox" id="dialog-vbox2"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">8</property> + + <child internal-child="action_area"> + <widget class="GtkHButtonBox" id="dialog-action_area2"> + <property name="visible">True</property> + <property name="layout_style">GTK_BUTTONBOX_END</property> + + <child> + <widget class="GtkButton" id="node_properties_cancel_button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-cancel</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-6</property> + </widget> + </child> + + <child> + <widget class="GtkButton" id="node_properties_ok_button"> + <property name="visible">True</property> + <property name="can_default">True</property> + <property name="can_focus">True</property> + <property name="label">gtk-ok</property> + <property name="use_stock">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + <property name="response_id">-5</property> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="pack_type">GTK_PACK_END</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="table1"> + <property name="visible">True</property> + <property name="n_rows">2</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">8</property> + <property name="column_spacing">4</property> + + <child> + <widget class="GtkSpinButton" id="node_properties_note_spinbutton"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">0</property> + <property name="numeric">True</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">60 0 127 1 10 10</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label6"> + <property name="visible">True</property> + <property name="label" translatable="yes">Note: </property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">0</property> + <property name="bottom_attach">1</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label7"> + <property name="visible">True</property> + <property name="label" translatable="yes">Duration: </property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property> + <property name="width_chars">-1</property> + <property name="single_line_mode">False</property> + <property name="angle">0</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkSpinButton" id="node_properties_duration_spinbutton"> + <property name="visible">True</property> + <property name="sensitive">False</property> + <property name="can_focus">True</property> + <property name="climb_rate">1</property> + <property name="digits">2</property> + <property name="numeric">True</property> + <property name="update_policy">GTK_UPDATE_ALWAYS</property> + <property name="snap_to_ticks">False</property> + <property name="wrap">False</property> + <property name="adjustment">1 0 999999 1 10 10</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="y_options"></property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + </glade-interface> diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 6f1fc4d..741c801 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -38,11 +38,19 @@ main(int argc, char** argv) SharedPtr<Machina::Machine> machine; // Load machine, if given - if (argc == 2) { - string filename = argv[1]; + if (argc >= 2) { + const string filename = argv[1]; cout << "Building machine from MIDI file " << filename << endl; SharedPtr<Machina::SMFDriver> file_driver(new Machina::SMFDriver()); - machine = file_driver->learn(filename); + + if (argc >= 3) { + float q = strtof(argv[2], NULL); + cout << "Quantization: " << q << endl; + machine = file_driver->learn(filename, q); + } else { + cout << "No quantization." << endl; + machine = file_driver->learn(filename); + } } if (!machine) |