From 9e77d8923ac49d7afb6e9d8a5e847e8f60449af0 Mon Sep 17 00:00:00 2001 From: Paul Davis Date: Wed, 27 Apr 2022 08:13:04 -0600 Subject: [PATCH] MIDI: ctrl-d now duplicates selected notes This commit also fixes selection-after-paste, so that the selection is always the newly pasted notes. --- gtk2_ardour/ardour.keys.in | 1 + gtk2_ardour/editor_actions.cc | 1 + gtk2_ardour/midi_region_view.cc | 53 +++++++++++++++++++++++++++++++-- gtk2_ardour/midi_region_view.h | 2 ++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/gtk2_ardour/ardour.keys.in b/gtk2_ardour/ardour.keys.in index efa023772d..4054118de0 100644 --- a/gtk2_ardour/ardour.keys.in +++ b/gtk2_ardour/ardour.keys.in @@ -487,6 +487,7 @@ This mode provides many different operations on both regions and control points, @notes|Notes/alt-add-select-previous|<@PRIMARY@><@TERTIARY@>ISO_Left_Tab|Add previous note to selection @notes|Notes/extend-selection|<@PRIMARY@>e|Extend selection to end of region @notes|Notes/invert-selection|<@PRIMARY@>i|Invert note selection +@notes|Notes/duplicate-selection|<@PRIMARY@>d|Duplicate note selection @rec|Recorder/arm-all|<@PRIMARY@>r|record arm all tracks @rec|Recorder/arm-none|<@PRIMARY@><@TERTIARY@>r|disable record arm of all tracks diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index 5db07f0085..a95f1c499c 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -798,6 +798,7 @@ Editor::register_midi_actions (Bindings* midi_bindings) ActionManager::register_action (_midi_actions, X_("clear-selection"), _("Clear Note Selection"), sigc::bind (sigc::mem_fun (*this, &Editor::midi_action), &MidiRegionView::clear_note_selection)); ActionManager::register_action (_midi_actions, X_("invert-selection"), _("Invert Note Selection"), sigc::bind (sigc::mem_fun (*this, &Editor::midi_action), &MidiRegionView::invert_selection)); ActionManager::register_action (_midi_actions, X_("extend-selection"), _("Extend Note Selection"), sigc::bind (sigc::mem_fun (*this, &Editor::midi_action), &MidiRegionView::extend_selection)); + ActionManager::register_action (_midi_actions, X_("duplicate-selection"), _("Duplicate Note Selection"), sigc::bind (sigc::mem_fun (*this, &Editor::midi_action), &MidiRegionView::duplicate_selection)); /* Lengthen */ diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index 4096140648..7cb1d07787 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -171,6 +171,7 @@ MidiRegionView::MidiRegionView (ArdourCanvas::Container* parent, , _last_event_y (0) , _entered (false) , _entered_note (0) + , _pasting (false) , _mouse_changed_selection (false) { CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name())); @@ -1935,7 +1936,9 @@ MidiRegionView::add_note(const boost::shared_ptr note, bool visible) } } - if (_marked_for_selection.find(note) != _marked_for_selection.end()) { + if (_pasting) { + note_selected (event, true); + } else if (_marked_for_selection.find(note) != _marked_for_selection.end()) { note_selected (event, false); } @@ -3800,20 +3803,61 @@ MidiRegionView::selection_as_cut_buffer () const return cb; } +void +MidiRegionView::duplicate_selection () +{ + std::cerr << "dup selection\n"; + + if (_selection.empty()) { + return; + } + + /* find last selection position */ + timepos_t dup_pos = timepos_t (Temporal::BeatTime); + + for (Selection::const_iterator s = _selection.begin(); s != _selection.end(); ++s) { + dup_pos = std::max (dup_pos, _region->source_beats_to_absolute_time ((*s)->note()->end_time())); + } + + PublicEditor& editor (trackview.editor()); + + /* Use a local Selection object that will not affect the global + * selection. Possible ::paste() should accept a different kind of + * object but that would conflict with the Editor API. + */ + + ::Selection local_selection (&editor, false); + MidiNoteSelection note_selection; + + note_selection.push_back (selection_as_cut_buffer()); + + local_selection.set (note_selection); + + PasteContext ctxt (0, 1, ItemCounts(), false); + paste (dup_pos, local_selection, ctxt); +} + /** This method handles undo */ bool MidiRegionView::paste (timepos_t const & pos, const ::Selection& selection, PasteContext& ctx) { bool commit = false; + // Paste notes, if available MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes()); if (m != selection.midi_notes.end()) { + ctx.counts.increase_n_notes(); + if (!(*m)->empty()) { commit = true; } - paste_internal(pos, ctx.count, ctx.times, **m); + + /* clear any current selection because we're going to select the duplicated ones */ + clear_note_selection (); + + paste_internal (pos, ctx.count, ctx.times, **m); } // Paste control points to automation children, if available @@ -3842,6 +3886,8 @@ MidiRegionView::paste_internal (timepos_t const & pos, unsigned paste_count, flo return; } + PBD::Unwinder puw (_pasting, true); + start_note_diff_command (_("paste")); const Temporal::Beats snap_beats = get_grid_beats(pos); @@ -3889,6 +3935,9 @@ MidiRegionView::paste_internal (timepos_t const & pos, unsigned paste_count, flo trackview.session()->add_command (new StatefulDiffCommand (_region)); } + _marked_for_selection.clear (); + _pending_note_selection.clear (); + apply_diff (true); } diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index f8c577ad58..46a19214b3 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -208,6 +208,7 @@ public: void select_range(Temporal::timepos_t const & start, Temporal::timepos_t const & end); void invert_selection (); void extend_selection (); + void duplicate_selection (); Temporal::Beats earliest_in_selection (); void move_selection(Temporal::timecnt_t const & dx, double dy, double cumulative_dy); @@ -545,6 +546,7 @@ public: double _last_event_y; bool _entered; NoteBase* _entered_note; + bool _pasting; bool _mouse_changed_selection;