From 563f5c11a61ebae6a988fc703f3d465f3d7cd25a Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 16 Nov 2014 22:35:37 -0500 Subject: Support cut/copy/paste of MIDI notes and controllers at the same time. --- gtk2_ardour/editor_drag.cc | 16 ++-------- gtk2_ardour/editor_ops.cc | 17 ++++++----- gtk2_ardour/item_counts.h | 6 ++++ gtk2_ardour/midi_region_view.cc | 66 +++++++++++++++++++++++++++++++++++++---- gtk2_ardour/midi_region_view.h | 6 ++-- gtk2_ardour/midi_selection.h | 14 ++++++++- 6 files changed, 95 insertions(+), 30 deletions(-) (limited to 'gtk2_ardour') diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 773ef191df..8c164783d6 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -5139,21 +5139,9 @@ MidiRubberbandSelectDrag::MidiRubberbandSelectDrag (Editor* e, MidiRegionView* r void MidiRubberbandSelectDrag::select_things (int button_state, framepos_t x1, framepos_t x2, double y1, double y2, bool /*drag_in_progress*/) { - framepos_t const p = _region_view->region()->position (); - double const y = _region_view->midi_view()->y_position (); - - x1 = max ((framepos_t) 0, x1 - p); - x2 = max ((framepos_t) 0, x2 - p); - y1 = max (0.0, y1 - y); - y2 = max (0.0, y2 - y); - _region_view->update_drag_selection ( - _editor->sample_to_pixel (x1), - _editor->sample_to_pixel (x2), - y1, - y2, - Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier) - ); + x1, x2, y1, y2, + Keyboard::modifier_state_contains (button_state, Keyboard::TertiaryModifier)); } void diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index ed1cc7b256..53d6de9f5e 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -4009,6 +4009,13 @@ Editor::cut_copy_midi (CutCopyOp op) _last_cut_copy_source_track = &mrv->get_time_axis_view(); } } + + if (!selection->points.empty()) { + cut_copy_points (op); + if (op == Cut || op == Delete) { + selection->clear_points (); + } + } } struct lt_playlist { @@ -4433,17 +4440,13 @@ Editor::paste_internal (framepos_t position, float times) /* undo/redo is handled by individual tracks/regions */ RegionSelection rs; - RegionSelection::iterator r; - MidiNoteSelection::iterator cb; - get_regions_at (rs, position, ts); - for (cb = cut_buffer->midi_notes.begin(), r = rs.begin(); - cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) { + ItemCounts counts; + for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) { MidiRegionView* mrv = dynamic_cast (*r); if (mrv) { - mrv->paste (position, paste_count, times, **cb); - ++cb; + mrv->paste (position, paste_count, times, *cut_buffer, counts); } } diff --git a/gtk2_ardour/item_counts.h b/gtk2_ardour/item_counts.h index b7c6dbd9c6..639fabd2cc 100644 --- a/gtk2_ardour/item_counts.h +++ b/gtk2_ardour/item_counts.h @@ -35,9 +35,12 @@ class ItemCounts { public: + ItemCounts() : _notes(0) {} + size_t n_playlists(ARDOUR::DataType t) const { return get_n(t, _playlists); } size_t n_regions(ARDOUR::DataType t) const { return get_n(t, _regions); } size_t n_lines(Evoral::Parameter t) const { return get_n(t, _lines); } + size_t n_notes() const { return _notes; } void increase_n_playlists(ARDOUR::DataType t, size_t delta=1) { increase_n(t, _playlists, delta); @@ -51,6 +54,8 @@ public: increase_n(t, _lines, delta); } + void increase_n_notes(size_t delta=1) { _notes += delta; } + private: template size_t @@ -73,6 +78,7 @@ private: std::map _playlists; std::map _regions; std::map _lines; + size_t _notes; }; #endif /* __ardour_item_counts_h__ */ diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc index d519bd1a04..cfb5108a60 100644 --- a/gtk2_ardour/midi_region_view.cc +++ b/gtk2_ardour/midi_region_view.cc @@ -49,11 +49,13 @@ #include "automation_region_view.h" #include "automation_time_axis.h" +#include "control_point.h" #include "debug.h" #include "editor.h" #include "editor_drag.h" #include "ghostregion.h" #include "gui_thread.h" +#include "item_counts.h" #include "keyboard.h" #include "midi_channel_dialog.h" #include "midi_cut_buffer.h" @@ -2287,8 +2289,18 @@ MidiRegionView::note_deselected(NoteBase* ev) } void -MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1, bool extend) +MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend) { + PublicEditor& editor = trackview.editor(); + + // Convert to local coordinates + const framepos_t p = _region->position(); + const double y = midi_view()->y_position(); + const double x0 = editor.sample_to_pixel(max((framepos_t)0, start - p)); + const double x1 = editor.sample_to_pixel(max((framepos_t)0, end - p)); + const double y0 = max(0.0, gy0 - y); + const double y1 = max(0.0, gy1 - y); + // TODO: Make this faster by storing the last updated selection rect, and only // adjusting things that are in the area that appears/disappeared. // We probably need a tree to be able to find events in O(log(n)) time. @@ -2304,6 +2316,24 @@ MidiRegionView::update_drag_selection(double x0, double x1, double y0, double y1 remove_from_selection (*i); } } + + typedef RouteTimeAxisView::AutomationTracks ATracks; + typedef std::list Selectables; + + /* Add control points to selection. */ + const ATracks& atracks = midi_view()->automation_tracks(); + Selectables selectables; + editor.get_selection().clear_points(); + for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) { + a->second->get_selectables(start, end, gy0, gy1, selectables); + for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) { + ControlPoint* cp = dynamic_cast(*s); + if (cp) { + editor.get_selection().add(cp); + } + } + a->second->set_selected_points(editor.get_selection().points); + } } void @@ -3324,9 +3354,37 @@ MidiRegionView::selection_as_cut_buffer () const return cb; } +/** This method handles undo */ +bool +MidiRegionView::paste (framepos_t pos, unsigned paste_count, float times, const ::Selection& selection, ItemCounts& counts) +{ + // Get our set of notes from the selection + MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(counts.n_notes()); + if (m == selection.midi_notes.end()) { + return false; + } + counts.increase_n_notes(); + + trackview.session()->begin_reversible_command (Operations::paste); + + // Paste notes + paste_internal(pos, paste_count, times, **m); + + // Paste control points to automation children + typedef RouteTimeAxisView::AutomationTracks ATracks; + const ATracks& atracks = midi_view()->automation_tracks(); + for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) { + a->second->paste(pos, paste_count, times, selection, counts); + } + + trackview.session()->commit_reversible_command (); + + return true; +} + /** This method handles undo */ void -MidiRegionView::paste (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb) +MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb) { if (mcb.empty()) { return; @@ -3334,8 +3392,6 @@ MidiRegionView::paste (framepos_t pos, unsigned paste_count, float times, const PublicEditor& editor = trackview.editor (); - trackview.session()->begin_reversible_command (Operations::paste); - start_note_diff_command (_("paste")); /* get snap duration, default to 1 beat if not snapped to anything musical */ @@ -3390,8 +3446,6 @@ MidiRegionView::paste (framepos_t pos, unsigned paste_count, float times, const } apply_diff (true); - - trackview.session()->commit_reversible_command (); } struct EventNoteTimeEarlyFirstComparator { diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h index cda4d5802c..9885b98f3a 100644 --- a/gtk2_ardour/midi_region_view.h +++ b/gtk2_ardour/midi_region_view.h @@ -60,6 +60,7 @@ class MidiListEditor; class EditNoteDialog; class NotePlayer; class PatchChange; +class ItemCounts; class MidiRegionView : public RegionView { @@ -112,7 +113,8 @@ public: void resolve_note(uint8_t note_num, double end_time); void cut_copy_clear (Editing::CutCopyOp); - void paste (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer&); + bool paste (framepos_t pos, unsigned paste_count, float times, const ::Selection& selection, ItemCounts& counts); + void paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer&); void add_canvas_patch_change (ARDOUR::MidiModel::PatchChangePtr patch, const std::string& displaytext, bool); @@ -366,7 +368,7 @@ private: ARDOUR::MidiModel::TimeType end_delta); void clear_selection_except (NoteBase* ev, bool signal = true); - void update_drag_selection (double last_x, double x, double last_y, double y, bool extend); + void update_drag_selection (framepos_t start, framepos_t end, double y0, double y1, bool extend); void update_vertical_drag_selection (double last_y, double y, bool extend); void add_to_selection (NoteBase*); diff --git a/gtk2_ardour/midi_selection.h b/gtk2_ardour/midi_selection.h index 2aa04356d7..6ee26e4487 100644 --- a/gtk2_ardour/midi_selection.h +++ b/gtk2_ardour/midi_selection.h @@ -35,6 +35,18 @@ public: MidiRegionSelection& operator= (const MidiRegionSelection&); }; -struct MidiNoteSelection : std::list {}; +struct MidiNoteSelection : std::list { +public: + const_iterator + get_nth(size_t nth) const { + size_t count = 0; + for (const_iterator m = begin(); m != end(); ++m) { + if (count++ == nth) { + return m; + } + } + return end(); + } +}; #endif /* __ardour_gtk_midi_selection_h__ */ -- cgit v1.2.3