std::string path;
bool requested;
bool mandatory;
+ XMLNode* state;
};
class ControlProtocolManager : public sigc::trackable, public Stateful
if ((*i)->requested || (*i)->mandatory) {
instantiate (**i);
(*i)->requested = false;
+
+ if ((*i)->state) {
+ (*i)->protocol->set_state (*(*i)->state);
+ }
}
}
}
cpi->protocol = 0;
cpi->requested = false;
cpi->mandatory = descriptor->mandatory;
+ cpi->state = 0;
control_protocol_info.push_back (cpi);
if (Controllable::StartLearning (&controllable)) {
string prompt = _("operate controller now");
prompter.set_text (prompt);
- prompter.show_all ();
+ prompter.touch (); // shows popup
+ learning_connection = controllable.LearningFinished.connect (mem_fun (*this, &BindingProxy::learning_finished));
}
return true;
}
return false;
}
+void
+BindingProxy::learning_finished ()
+{
+ learning_connection.disconnect ();
+ prompter.touch (); // hides popup
+}
+
+
bool
BindingProxy::prompter_hiding (GdkEventAny *ev)
{
+ learning_connection.disconnect ();
Controllable::StopLearning (&controllable);
return false;
}
PBD::Controllable& controllable;
guint bind_button;
guint bind_statemask;
-
+ sigc::connection learning_connection;
+ void learning_finished ();
bool prompter_hiding (GdkEventAny *);
};
{
hide ();
+ if (popdown_time != 0 && timeout != -1) {
+ gtk_timeout_remove (timeout);
+ }
+
if (delete_on_hide) {
std::cerr << "deleting prompter\n";
- if (popdown_time != 0 && timeout != -1) {
- gtk_timeout_remove (timeout);
- }
gtk_idle_add (idle_delete, this);
}
}
{
hide();
+ if (popdown_time != 0 && timeout != -1) {
+ gtk_timeout_remove (timeout);
+ }
+
if (delete_on_hide) {
std::cerr << "deleting prompter\n" << endl;
- if (popdown_time != 0 && timeout != -1) {
- gtk_timeout_remove (timeout);
- }
gtk_idle_add (idle_delete, this);
}
virtual bool can_send_feedback() const { return true; }
+ sigc::signal<void> LearningFinished;
+
static sigc::signal<void,Controllable*> Created;
static sigc::signal<void,Controllable*> GoingAway;
#include <vector>
#include <list>
#include <sigc++/sigc++.h>
-
+#include <pbd/stateful.h>
#include <control_protocol/basic_ui.h>
namespace ARDOUR {
class Route;
class Session;
-class ControlProtocol : public sigc::trackable, public BasicUI {
+class ControlProtocol : public sigc::trackable, public Stateful, public BasicUI {
public:
ControlProtocol (Session&, std::string name);
virtual ~ControlProtocol();
#include <algorithm>
+#include <pbd/error.h>
+#include <pbd/failed_constructor.h>
+
#include <midi++/port.h>
#include <midi++/manager.h>
#include <midi++/port_request.h>
: ControlProtocol (s, _("GenericMIDI"))
{
MIDI::Manager* mm = MIDI::Manager::instance();
- MIDI::PortRequest pr ("ardour:MIDI control", "ardour:MIDI control", "duplex", "alsa/seq");
+
+ /* XXX it might be nice to run "control" through i18n, but thats a bit tricky because
+ the name is defined in ardour.rc which is likely not internationalized.
+ */
- _port = mm->add_port (pr);
+ _port = mm->port (X_("control"));
+
+ if (_port == 0) {
+ error << _("no MIDI port named \"control\" exists - generic MIDI control disabled") << endmsg;
+ throw failed_constructor();
+ }
_feedback_interval = 10000; // microseconds
last_feedback_time = 0;
MIDIControllable* mc = new MIDIControllable (*_port, *c);
-
{
Glib::Mutex::Lock lm (pending_lock);
- pending_controllables.push_back (mc);
- mc->learning_stopped.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc));
+ std::pair<MIDIControllables::iterator,bool> result;
+ result = pending_controllables.insert (mc);
+ if (result.second) {
+ c->LearningFinished.connect (bind (mem_fun (*this, &GenericMidiControlProtocol::learning_stopped), mc));
+ }
}
mc->learn_about_external_control ();
pending_controllables.erase (i);
}
- controllables.push_back (mc);
+ controllables.insert (mc);
}
void
}
}
}
+
+XMLNode&
+GenericMidiControlProtocol::get_state ()
+{
+ XMLNode* node = new XMLNode (_name); /* node name must match protocol name */
+ XMLNode* children = new XMLNode (X_("controls"));
+
+ node->add_child_nocopy (*children);
+
+ Glib::Mutex::Lock lm2 (controllables_lock);
+ for (MIDIControllables::iterator i = controllables.begin(); i != controllables.end(); ++i) {
+ children->add_child_nocopy ((*i)->get_state());
+ }
+
+ return *node;
+}
+
+int
+GenericMidiControlProtocol::set_state (const XMLNode& node)
+{
+ XMLNodeList nlist;
+ XMLNodeConstIterator niter;
+ Controllable* c;
+
+ {
+ Glib::Mutex::Lock lm (pending_lock);
+ pending_controllables.clear ();
+ }
+
+ Glib::Mutex::Lock lm2 (controllables_lock);
+
+ controllables.clear ();
+
+ nlist = node.children();
+
+ if (nlist.empty()) {
+ return 0;
+ }
+
+ nlist = nlist.front()->children ();
+
+ for (niter = nlist.begin(); niter != nlist.end(); ++niter) {
+
+ XMLProperty* prop;
+
+ if ((prop = (*niter)->property ("id")) != 0) {
+
+ ID id = prop->value ();
+
+ c = session->controllable_by_id (id);
+
+ if (c) {
+ MIDIControllable* mc = new MIDIControllable (*_port, *c);
+ if (mc->set_state (**niter) == 0) {
+ controllables.insert (mc);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
#ifndef ardour_generic_midi_control_protocol_h
#define ardour_generic_midi_control_protocol_h
-#include <vector>
+#include <set>
#include <glibmm/thread.h>
#include <ardour/types.h>
MIDI::Port* port () const { return _port; }
void set_feedback_interval (ARDOUR::microseconds_t);
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+
private:
MIDI::Port* _port;
ARDOUR::microseconds_t _feedback_interval;
void _send_feedback ();
void send_feedback ();
- typedef std::vector<MIDIControllable*> MIDIControllables;
+ typedef std::set<MIDIControllable*> MIDIControllables;
MIDIControllables controllables;
MIDIControllables pending_controllables;
Glib::Mutex controllables_lock;
+#include <pbd/failed_constructor.h>
+
#include <control_protocol/control_protocol.h>
#include "generic_midi_control_protocol.h"
ControlProtocol*
new_generic_midi_protocol (ControlProtocolDescriptor* descriptor, Session* s)
{
- GenericMidiControlProtocol* gmcp = new GenericMidiControlProtocol (*s);
+ GenericMidiControlProtocol* gmcp;
+
+ try {
+ gmcp = new GenericMidiControlProtocol (*s);
+ } catch (failed_constructor& err) {
+ return 0;
+ }
if (gmcp->set_active (true)) {
delete gmcp;
{
drop_external_control ();
midi_learn_connection = _port.input()->any.connect (mem_fun (*this, &MIDIControllable::midi_receiver));
- learning_started ();
}
void
bind_midi ((channel_t) (msg[0] & 0xf), eventType (msg[0] & 0xF0), msg[1]);
- learning_stopped ();
+ controllable.LearningFinished ();
}
void
void stop_learning ();
void drop_external_control ();
- sigc::signal<void> learning_started;
- sigc::signal<void> learning_stopped;
-
bool get_midi_feedback () { return feedback; }
void set_midi_feedback (bool val) { feedback = val; }
}
}
+XMLNode&
+TranzportControlProtocol::get_state ()
+{
+ XMLNode* node = new XMLNode (_name); /* node name must match protocol name */
+ return *node;
+}
+
+int
+TranzportControlProtocol::set_state (const XMLNode& node)
+{
+ return 0;
+}
static bool probe ();
+ XMLNode& get_state ();
+ int set_state (const XMLNode&);
+
private:
static const int VENDORID = 0x165b;
static const int PRODUCTID = 0x8101;