+ XMLNode* diff_command = new XMLNode (NOTE_DIFF_COMMAND_ELEMENT);
+ diff_command->add_property("midi-source", _model->midi_source()->id().to_s());
+
+ XMLNode* changes = diff_command->add_child(DIFF_NOTES_ELEMENT);
+ for_each(_changes.begin(), _changes.end(),
+ boost::bind (
+ boost::bind (&XMLNode::add_child_nocopy, changes, _1),
+ boost::bind (&NoteDiffCommand::marshal_change, this, _1)));
+
+ XMLNode* added_notes = diff_command->add_child(ADDED_NOTES_ELEMENT);
+ for_each(_added_notes.begin(), _added_notes.end(),
+ boost::bind(
+ boost::bind (&XMLNode::add_child_nocopy, added_notes, _1),
+ boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
+
+ XMLNode* removed_notes = diff_command->add_child(REMOVED_NOTES_ELEMENT);
+ for_each(_removed_notes.begin(), _removed_notes.end(),
+ boost::bind (
+ boost::bind (&XMLNode::add_child_nocopy, removed_notes, _1),
+ boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
+
+ /* if this command had side-effects, store that state too
+ */
+
+ if (!side_effect_removals.empty()) {
+ XMLNode* side_effect_notes = diff_command->add_child(SIDE_EFFECT_REMOVALS_ELEMENT);
+ for_each(side_effect_removals.begin(), side_effect_removals.end(),
+ boost::bind (
+ boost::bind (&XMLNode::add_child_nocopy, side_effect_notes, _1),
+ boost::bind (&NoteDiffCommand::marshal_note, this, _1)));
+ }
+
+ return *diff_command;
+}
+
+MidiModel::SysExDiffCommand::SysExDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode& node)
+ : DiffCommand (m, "")
+{
+ assert (_model);
+ set_state (node, Stateful::loading_state_version);
+}
+
+void
+MidiModel::SysExDiffCommand::change (boost::shared_ptr<Evoral::Event<TimeType> > s, TimeType new_time)
+{
+ Change change;
+
+ change.sysex = s;
+ change.property = Time;
+ change.old_time = s->time ();
+ change.new_time = new_time;
+
+ _changes.push_back (change);
+}
+
+void
+MidiModel::SysExDiffCommand::operator() ()
+{
+ {
+ MidiModel::WriteLock lock (_model->edit_lock ());
+
+ for (list<SysExPtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
+ _model->remove_sysex_unlocked (*i);
+ }
+
+ /* find any sysex events that were missing when unmarshalling */
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ if (!i->sysex) {
+ i->sysex = _model->find_sysex (i->sysex_id);
+ assert (i->sysex);
+ }
+ }
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ switch (i->property) {
+ case Time:
+ i->sysex->set_time (i->new_time);
+ }
+ }
+ }
+
+ _model->ContentsChanged (); /* EMIT SIGNAL */
+}
+
+void
+MidiModel::SysExDiffCommand::undo ()
+{
+ {
+ MidiModel::WriteLock lock (_model->edit_lock ());
+
+ for (list<SysExPtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
+ _model->add_sysex_unlocked (*i);
+ }
+
+ /* find any sysex events that were missing when unmarshalling */
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ if (!i->sysex) {
+ i->sysex = _model->find_sysex (i->sysex_id);
+ assert (i->sysex);
+ }
+ }
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ switch (i->property) {
+ case Time:
+ i->sysex->set_time (i->old_time);
+ break;
+ }
+ }
+
+ }
+
+ _model->ContentsChanged(); /* EMIT SIGNAL */
+}
+
+void
+MidiModel::SysExDiffCommand::remove (SysExPtr sysex)
+{
+ _removed.push_back(sysex);
+}
+
+XMLNode&
+MidiModel::SysExDiffCommand::marshal_change (const Change& change)
+{
+ XMLNode* xml_change = new XMLNode ("Change");
+
+ /* first, the change itself */
+
+ xml_change->add_property ("property", enum_2_string (change.property));
+
+ {
+ ostringstream old_value_str (ios::ate);
+ old_value_str << change.old_time;
+ xml_change->add_property ("old", old_value_str.str());
+ }
+
+ {
+ ostringstream new_value_str (ios::ate);
+ new_value_str << change.new_time;
+ xml_change->add_property ("new", new_value_str.str());
+ }
+
+ ostringstream id_str;
+ id_str << change.sysex->id();
+ xml_change->add_property ("id", id_str.str());
+
+ return *xml_change;
+}
+
+MidiModel::SysExDiffCommand::Change
+MidiModel::SysExDiffCommand::unmarshal_change (XMLNode *xml_change)
+{
+ XMLProperty const * prop;
+ Change change;
+
+ if ((prop = xml_change->property ("property")) != 0) {
+ change.property = (Property) string_2_enum (prop->value(), change.property);
+ } else {
+ fatal << "!!!" << endmsg;
+ abort(); /*NOTREACHED*/
+ }
+
+ if ((prop = xml_change->property ("id")) == 0) {
+ error << _("No SysExID found for sys-ex property change - ignored") << endmsg;
+ return change;
+ }
+
+ gint sysex_id = atoi (prop->value().c_str());
+
+ if ((prop = xml_change->property ("old")) != 0) {
+ istringstream old_str (prop->value());
+ old_str >> change.old_time;
+ } else {
+ fatal << "!!!" << endmsg;
+ abort(); /*NOTREACHED*/
+ }
+
+ if ((prop = xml_change->property ("new")) != 0) {
+ istringstream new_str (prop->value());
+ new_str >> change.new_time;
+ } else {
+ fatal << "!!!" << endmsg;
+ abort(); /*NOTREACHED*/
+ }
+
+ /* we must point at the instance of the sysex that is actually in the model.
+ so go look for it ...
+ */
+
+ change.sysex = _model->find_sysex (sysex_id);
+ change.sysex_id = sysex_id;
+
+ return change;
+}
+
+int
+MidiModel::SysExDiffCommand::set_state (const XMLNode& diff_command, int /*version*/)
+{
+ if (diff_command.name() != string (SYSEX_DIFF_COMMAND_ELEMENT)) {
+ return 1;
+ }
+
+ /* changes */
+
+ _changes.clear();
+
+ XMLNode* changed_sysexes = diff_command.child (DIFF_SYSEXES_ELEMENT);
+
+ if (changed_sysexes) {
+ XMLNodeList sysexes = changed_sysexes->children();
+ transform (sysexes.begin(), sysexes.end(), back_inserter (_changes),
+ boost::bind (&SysExDiffCommand::unmarshal_change, this, _1));
+
+ }
+
+ return 0;
+}
+
+XMLNode&
+MidiModel::SysExDiffCommand::get_state ()
+{
+ XMLNode* diff_command = new XMLNode (SYSEX_DIFF_COMMAND_ELEMENT);
+ diff_command->add_property ("midi-source", _model->midi_source()->id().to_s());
+
+ XMLNode* changes = diff_command->add_child(DIFF_SYSEXES_ELEMENT);
+ for_each (_changes.begin(), _changes.end(),
+ boost::bind (
+ boost::bind (&XMLNode::add_child_nocopy, changes, _1),
+ boost::bind (&SysExDiffCommand::marshal_change, this, _1)));
+
+ return *diff_command;
+}
+
+MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const string& name)
+ : DiffCommand (m, name)
+{
+ assert (_model);
+}
+
+MidiModel::PatchChangeDiffCommand::PatchChangeDiffCommand (boost::shared_ptr<MidiModel> m, const XMLNode & node)
+ : DiffCommand (m, "")
+{
+ assert (_model);
+ set_state (node, Stateful::loading_state_version);
+}
+
+void
+MidiModel::PatchChangeDiffCommand::add (PatchChangePtr p)
+{
+ _added.push_back (p);
+}
+
+void
+MidiModel::PatchChangeDiffCommand::remove (PatchChangePtr p)
+{
+ _removed.push_back (p);
+}
+
+void
+MidiModel::PatchChangeDiffCommand::change_time (PatchChangePtr patch, TimeType t)
+{
+ Change c;
+ c.property = Time;
+ c.patch = patch;
+ c.old_time = patch->time ();
+ c.new_time = t;
+
+ _changes.push_back (c);
+}
+
+void
+MidiModel::PatchChangeDiffCommand::change_channel (PatchChangePtr patch, uint8_t channel)
+{
+ Change c;
+ c.property = Channel;
+ c.patch = patch;
+ c.old_channel = patch->channel ();
+ c.new_channel = channel;
+ c.patch_id = patch->id();
+
+ _changes.push_back (c);
+}
+
+void
+MidiModel::PatchChangeDiffCommand::change_program (PatchChangePtr patch, uint8_t program)
+{
+ Change c;
+ c.property = Program;
+ c.patch = patch;
+ c.old_program = patch->program ();
+ c.new_program = program;
+ c.patch_id = patch->id();
+
+ _changes.push_back (c);
+}
+
+void
+MidiModel::PatchChangeDiffCommand::change_bank (PatchChangePtr patch, int bank)
+{
+ Change c;
+ c.property = Bank;
+ c.patch = patch;
+ c.old_bank = patch->bank ();
+ c.new_bank = bank;
+
+ _changes.push_back (c);
+}
+
+void
+MidiModel::PatchChangeDiffCommand::operator() ()
+{
+ {
+ MidiModel::WriteLock lock (_model->edit_lock ());
+
+ for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
+ _model->add_patch_change_unlocked (*i);
+ }
+
+ for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
+ _model->remove_patch_change_unlocked (*i);
+ }
+
+ /* find any patch change events that were missing when unmarshalling */
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ if (!i->patch) {
+ i->patch = _model->find_patch_change (i->patch_id);
+ assert (i->patch);
+ }
+ }
+
+ set<PatchChangePtr> temporary_removals;
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ switch (i->property) {
+ case Time:
+ if (temporary_removals.find (i->patch) == temporary_removals.end()) {
+ _model->remove_patch_change_unlocked (i->patch);
+ temporary_removals.insert (i->patch);
+ }
+ i->patch->set_time (i->new_time);
+ break;
+
+ case Channel:
+ i->patch->set_channel (i->new_channel);
+ break;
+
+ case Program:
+ i->patch->set_program (i->new_program);
+ break;
+
+ case Bank:
+ i->patch->set_bank (i->new_bank);
+ break;
+ }
+ }
+
+ for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
+ _model->add_patch_change_unlocked (*i);
+ }
+ }
+
+ _model->ContentsChanged (); /* EMIT SIGNAL */
+}
+
+void
+MidiModel::PatchChangeDiffCommand::undo ()
+{
+ {
+ MidiModel::WriteLock lock (_model->edit_lock());
+
+ for (list<PatchChangePtr>::iterator i = _added.begin(); i != _added.end(); ++i) {
+ _model->remove_patch_change_unlocked (*i);
+ }
+
+ for (list<PatchChangePtr>::iterator i = _removed.begin(); i != _removed.end(); ++i) {
+ _model->add_patch_change_unlocked (*i);
+ }
+
+ /* find any patch change events that were missing when unmarshalling */
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ if (!i->patch) {
+ i->patch = _model->find_patch_change (i->patch_id);
+ assert (i->patch);
+ }
+ }
+
+ set<PatchChangePtr> temporary_removals;
+
+ for (ChangeList::iterator i = _changes.begin(); i != _changes.end(); ++i) {
+ switch (i->property) {
+ case Time:
+ if (temporary_removals.find (i->patch) == temporary_removals.end()) {
+ _model->remove_patch_change_unlocked (i->patch);
+ temporary_removals.insert (i->patch);
+ }
+ i->patch->set_time (i->old_time);
+ break;
+
+ case Channel:
+ i->patch->set_channel (i->old_channel);
+ break;
+
+ case Program:
+ i->patch->set_program (i->old_program);
+ break;
+
+ case Bank:
+ i->patch->set_bank (i->old_bank);
+ break;
+ }
+ }
+
+ for (set<PatchChangePtr>::iterator i = temporary_removals.begin(); i != temporary_removals.end(); ++i) {
+ _model->add_patch_change_unlocked (*i);
+ }
+
+ }
+
+ _model->ContentsChanged (); /* EMIT SIGNAL */
+}
+
+XMLNode &
+MidiModel::PatchChangeDiffCommand::marshal_patch_change (constPatchChangePtr p)
+{
+ XMLNode* n = new XMLNode ("patch-change");
+
+ {
+ ostringstream s (ios::ate);
+ s << int (p->id ());
+ n->add_property ("id", s.str());
+ }
+
+ {
+ ostringstream s (ios::ate);
+ s << p->time ();
+ n->add_property ("time", s.str ());
+ }
+
+ {
+ ostringstream s (ios::ate);
+ s << int (p->channel ());
+ n->add_property ("channel", s.str ());
+ }
+
+ {
+ ostringstream s (ios::ate);
+ s << int (p->program ());
+ n->add_property ("program", s.str ());
+ }
+
+ {
+ ostringstream s (ios::ate);
+ s << int (p->bank ());
+ n->add_property ("bank", s.str ());
+ }
+
+ return *n;
+}
+
+XMLNode&
+MidiModel::PatchChangeDiffCommand::marshal_change (const Change& c)
+{
+ XMLNode* n = new XMLNode (X_("Change"));
+
+ n->add_property (X_("property"), enum_2_string (c.property));
+
+ {
+ ostringstream s (ios::ate);
+ if (c.property == Time) {
+ s << c.old_time;
+ } else if (c.property == Channel) {
+ s << c.old_channel;
+ } else if (c.property == Program) {
+ s << int (c.old_program);
+ } else if (c.property == Bank) {
+ s << c.old_bank;
+ }
+
+ n->add_property (X_("old"), s.str ());
+ }
+
+ {
+ ostringstream s (ios::ate);