Support cut/copy/paste of MIDI notes and controllers at the same time.
This commit is contained in:
parent
2fa6caad95
commit
563f5c11a6
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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__ */
|
||||
|
@ -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 {
|
||||
|
@ -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*);
|
||||
|
@ -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__ */
|
||||
|
Loading…
Reference in New Issue
Block a user