From ad6de3c2337120925d24d33b98bf8e2f669cdcd9 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Thu, 2 Jul 2020 23:15:10 -0600 Subject: [PATCH] when carrying out MIDI editing operations, be sure to act only once per Model when the selection involves non-forked copies Still can fail if the non-forked copies have been modified enough, but user should not multi-select them in that instance --- gtk2_ardour/editor.h | 1 + gtk2_ardour/editor_ops.cc | 82 +++++++++++++++++++++++++++++---------- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 801a7beb2d..70a7c4791e 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -2330,6 +2330,7 @@ private: /* MIDI actions, proxied to selected MidiRegionView(s) */ void midi_action (void (MidiRegionView::*method)()); + std::vector filter_to_unique_midi_region_views (RegionSelection const & ms) const; /* private helper functions to help with registering region actions */ diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 08dd4b4220..faee9abbac 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -5566,25 +5566,19 @@ Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs) bool in_command = false; - for (RegionSelection::const_iterator r = rs.begin(); r != rs.end(); ) { - RegionSelection::const_iterator tmp = r; - ++tmp; + vector views = filter_to_unique_midi_region_views (rs); - MidiRegionView* const mrv = dynamic_cast (*r); + for (vector::iterator mrv = views.begin(); mrv != views.end(); ++mrv) { - if (mrv) { - Command* cmd = apply_midi_note_edit_op_to_region (op, *mrv); - if (cmd) { - if (!in_command) { - begin_reversible_command (op.name ()); - in_command = true; - } - (*cmd)(); - _session->add_command (cmd); + Command* cmd = apply_midi_note_edit_op_to_region (op, **mrv); + if (cmd) { + if (!in_command) { + begin_reversible_command (op.name ()); + in_command = true; + } + (*cmd)(); + _session->add_command (cmd); } - } - - r = tmp; } if (in_command) { @@ -8567,12 +8561,24 @@ Editor::toggle_layer_display () } -void -Editor::midi_action (void (MidiRegionView::*method)()) +vector +Editor::filter_to_unique_midi_region_views (RegionSelection const & ms) const { - MidiRegionSelection ms = selection->midi_regions(); + typedef std::pair,samplepos_t> MapEntry; + std::set single_region_set; - for (MidiRegionSelection::iterator i = ms.begin(); i != ms.end(); ++i) { + vector views; + + /* build a list of regions that are unique with respect to their source + * and start position. Note: this is non-exhaustive... if someone has a + * non-forked copy of a MIDI region and then suitably modifies it, this + * will still put both regions into the list of things to be acted + * upon. + * + * Solution: user should not select both regions, or should fork one of them. + */ + + for (MidiRegionSelection::const_iterator i = ms.begin(); i != ms.end(); ++i) { MidiRegionView* mrv = dynamic_cast (*i); @@ -8580,6 +8586,40 @@ Editor::midi_action (void (MidiRegionView::*method)()) continue; } - (mrv->*method) (); + MapEntry entry = make_pair (mrv->midi_region()->midi_source(), mrv->region()->start()); + + if (single_region_set.insert (entry).second) { + views.push_back (mrv); + } + } + + return views; +} + + +void +Editor::midi_action (void (MidiRegionView::*method)()) +{ + MidiRegionSelection ms = selection->midi_regions(); + + if (ms.empty()) { + return; + } + + if (ms.size() > 1) { + + vector views = filter_to_unique_midi_region_views (ms); + + for (vector::iterator mrv = views.begin(); mrv != views.end(); ++mrv) { + ((*mrv)->*method) (); + } + + } else { + + MidiRegionView* mrv = dynamic_cast(ms.front()); + + if (mrv) { + (mrv->*method)(); + } } }