Support cut/copy/paste of MIDI notes and controllers at the same time.
authorDavid Robillard <d@drobilla.net>
Mon, 17 Nov 2014 03:35:37 +0000 (22:35 -0500)
committerDavid Robillard <d@drobilla.net>
Mon, 17 Nov 2014 03:35:45 +0000 (22:35 -0500)
gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/item_counts.h
gtk2_ardour/midi_region_view.cc
gtk2_ardour/midi_region_view.h
gtk2_ardour/midi_selection.h

index 773ef191dfafdc49db1ef81979ed750fef4f298a..8c164783d6c9555b9a75021eb91253acaec58eaa 100644 (file)
@@ -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
index ed1cc7b25699a0a682fc45022338cc2924583b3c..53d6de9f5e2d605b4d5824b2b8aac1017905f6fb 100644 (file)
@@ -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<MidiRegionView*> (*r);
                        if (mrv) {
-                               mrv->paste (position, paste_count, times, **cb);
-                               ++cb;
+                               mrv->paste (position, paste_count, times, *cut_buffer, counts);
                        }
                }
 
index b7c6dbd9c6213cc3daeb9c94c71457e2ee9d951f..639fabd2cc5c3d29461c629e68fb1cea3a8d6d24 100644 (file)
 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<typename Key>
        size_t
@@ -73,6 +78,7 @@ private:
        std::map<ARDOUR::DataType,  size_t> _playlists;
        std::map<ARDOUR::DataType,  size_t> _regions;
        std::map<Evoral::Parameter, size_t> _lines;
+       size_t                              _notes;
 };
 
 #endif /* __ardour_item_counts_h__ */
index d519bd1a0485f1654d078fb0f262fbd366dd191c..cfb5108a6060aba3ba5998b2b2015848637f5f3b 100644 (file)
 
 #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<Selectable*>              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<ControlPoint*>(*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 {
index cda4d5802c6b1fb6219560c60fe8a864a57f35b7..9885b98f3a93e6aa9aa027764c98444e639ccfe0 100644 (file)
@@ -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*);
index 2aa04356d710bfd375f6201f7fe18769f224b04d..6ee26e4487c549cf118a669019fe9b9b7122674d 100644 (file)
@@ -35,6 +35,18 @@ public:
        MidiRegionSelection& operator= (const MidiRegionSelection&);
 };
 
-struct MidiNoteSelection   : std::list<MidiCutBuffer*> {};
+struct MidiNoteSelection : std::list<MidiCutBuffer*> {
+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__ */