Support cut/copy/paste of MIDI notes and controllers at the same time.

This commit is contained in:
David Robillard 2014-11-16 22:35:37 -05:00
parent 2fa6caad95
commit 563f5c11a6
6 changed files with 95 additions and 30 deletions

View 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

View 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);
}
}

View File

@ -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__ */

View File

@ -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
@ -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 {

View 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*);

View 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__ */