summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2014-11-16 22:35:37 -0500
committerDavid Robillard <d@drobilla.net>2014-11-16 22:35:45 -0500
commit563f5c11a61ebae6a988fc703f3d465f3d7cd25a (patch)
tree1954fa25024c7bd100b04d5db354535a1b536a6d
parent2fa6caad95d81f058326d931532f687a157361be (diff)
Support cut/copy/paste of MIDI notes and controllers at the same time.
-rw-r--r--gtk2_ardour/editor_drag.cc16
-rw-r--r--gtk2_ardour/editor_ops.cc17
-rw-r--r--gtk2_ardour/item_counts.h6
-rw-r--r--gtk2_ardour/midi_region_view.cc66
-rw-r--r--gtk2_ardour/midi_region_view.h6
-rw-r--r--gtk2_ardour/midi_selection.h14
6 files changed, 95 insertions, 30 deletions
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<MidiRegionView*> (*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<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__ */
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<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
@@ -3325,8 +3355,36 @@ MidiRegionView::selection_as_cut_buffer () const
}
/** 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<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__ */