refactor code associated with cut/copy/clear so that pianoroll ops will affect automation points there

This commit is contained in:
Paul Davis
2025-09-14 13:49:07 -06:00
parent b65a70e580
commit 521f01984b
7 changed files with 178 additions and 15 deletions

View File

@@ -498,6 +498,15 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider,
bool dragging_playhead () const { return _dragging_playhead; }
struct AutomationRecord {
AutomationRecord () : state (0) , line (nullptr) {}
AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
XMLNode* state; ///< state before any operation
const AutomationLine* line; ///< line this came from
std::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
};
protected:
std::string _name;
bool within_track_canvas;
@@ -842,4 +851,3 @@ class EditingContext : public ARDOUR::SessionHandlePtr, public AxisViewProvider,
bool temporary_zoom_focus_change;
bool _dragging_playhead;
};

View File

@@ -4463,16 +4463,6 @@ Editor::cut_copy (CutCopyOp op)
}
}
struct AutomationRecord {
AutomationRecord () : state (0) , line (nullptr) {}
AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {}
XMLNode* state; ///< state before any operation
const AutomationLine* line; ///< line this came from
std::shared_ptr<Evoral::ControlList> copy; ///< copied events for the cut buffer
};
struct PointsSelectionPositionSorter {
bool operator() (ControlPoint* a, ControlPoint* b) {
return (*(a->model()))->when < (*(b->model()))->when;
@@ -4602,7 +4592,7 @@ Editor::cut_copy_midi (CutCopyOp op)
if (!mrv->selection().empty()) {
earliest = std::min (earliest, (*mrv->selection().begin())->note()->time());
}
mrv->cut_copy_clear (op);
mrv->cut_copy_clear (*selection, op);
/* XXX: not ideal, as there may be more than one track involved in the selection */
_last_cut_copy_source_track = &mrv->get_time_axis_view();

View File

@@ -4034,7 +4034,7 @@ MidiView::instrument_settings_changed ()
}
void
MidiView::cut_copy_clear (Editing::CutCopyOp op)
MidiView::cut_copy_clear (::Selection& /* ignored */, Editing::CutCopyOp op)
{
if (_selection.empty()) {
return;

View File

@@ -132,7 +132,7 @@ class MidiView : public virtual sigc::trackable, public LineMerger
NoteBase* add_note(const std::shared_ptr<NoteType> note, bool visible);
void cut_copy_clear (Editing::CutCopyOp);
virtual void cut_copy_clear (::Selection&, Editing::CutCopyOp);
bool paste (Temporal::timepos_t const & pos, const ::Selection& selection, PasteContext& ctx);
void paste_internal (Temporal::timepos_t const & pos, unsigned paste_count, float times, const MidiCutBuffer&);

View File

@@ -1715,7 +1715,7 @@ Pianoroll::cut_copy (Editing::CutCopyOp op)
case MouseContent:
if (view) {
begin_reversible_command (opname + ' ' + X_("MIDI"));
view->cut_copy_clear (op);
view->cut_copy_clear (*selection, op);
commit_reversible_command ();
}
return;

View File

@@ -30,6 +30,7 @@
#include "canvas/debug.h"
#include "canvas/text.h"
#include "control_point.h"
#include "editing_context.h"
#include "editor_drag.h"
#include "hit.h"
@@ -760,3 +761,162 @@ PianorollMidiView::hide_overlay_text ()
overlay_text->hide ();
}
}
void
PianorollMidiView::cut_copy_clear (::Selection& selection, Editing::CutCopyOp op)
{
MidiView::cut_copy_clear (selection, op);
cut_copy_points (op, timepos_t::zero (Temporal::BeatTime));
}
struct PointsSelectionPositionSorter {
bool operator() (ControlPoint* a, ControlPoint* b) {
return (*(a->model()))->when < (*(b->model()))->when;
}
};
/** Cut, copy or clear selected automation points.
* @param op Operation (Cut, Copy or Clear)
*/
void
PianorollMidiView::cut_copy_points (Editing::CutCopyOp op, timepos_t const & earliest_time)
{
using namespace Editing;
::Selection& selection (_editing_context.get_selection());
if (selection.points.empty ()) {
return;
}
timepos_t earliest (earliest_time);
/* Keep a record of the AutomationLists that we end up using in this operation */
typedef std::map<std::shared_ptr<AutomationList>, EditingContext::AutomationRecord> Lists;
Lists lists;
/* user could select points in any order */
selection.points.sort (PointsSelectionPositionSorter ());
/* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
for (auto & selected_point : selection.points) {
const AutomationLine& line (selected_point->line());
const std::shared_ptr<AutomationList> al = line.the_list();
if (lists.find (al) == lists.end ()) {
/* We haven't seen this list yet, so make a record for it. This includes
taking a copy of its current state, in case this is needed for undo later.
*/
lists[al] = EditingContext::AutomationRecord (&al->get_state (), &line);
}
}
if (op == Cut || op == Copy) {
/* This operation will involve putting things in the cut buffer, so create an empty
ControlList for each of our source lists to put the cut buffer data in.
*/
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
i->second.copy = i->first->create (i->first->parameter (), i->first->descriptor(), *i->first);
}
/* Add all selected points to the relevant copy ControlLists */
for (auto & selected_point : selection.points) {
std::shared_ptr<AutomationList> al = selected_point->line().the_list();
AutomationList::const_iterator ctrl_evt = selected_point->model ();
lists[al].copy->fast_simple_add ((*ctrl_evt)->when, (*ctrl_evt)->value);
earliest = std::min (earliest, (*ctrl_evt)->when);
}
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
/* Correct this copy list so that it is relative to the earliest
start time, so relative ordering between points is preserved
when copying from several lists and the paste starts at the
earliest copied piece of data.
*/
std::shared_ptr<Evoral::ControlList> &al_cpy = i->second.copy;
for (AutomationList::iterator ctrl_evt = al_cpy->begin(); ctrl_evt != al_cpy->end(); ++ctrl_evt) {
(*ctrl_evt)->when.shift_earlier (earliest);
}
/* And add it to the cut buffer */
_editing_context.get_cut_buffer().add (al_cpy);
}
}
if (op == Delete || op == Cut) {
/* This operation needs to remove things from the main AutomationList, so do that now */
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
i->first->freeze ();
}
/* Remove each selected point from its AutomationList */
for (auto & selected_point : selection.points) {
AutomationLine& line (selected_point->line ());
std::shared_ptr<AutomationList> al = line.the_list();
al->erase (selected_point->model ());
}
/* Thaw the lists and add undo records for them */
for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
std::shared_ptr<AutomationList> al = i->first;
al->thaw ();
_editing_context.add_command (new MementoCommand<AutomationList> (*al.get(), i->second.state, &(al->get_state ())));
}
}
}
void
PianorollMidiView::cut_copy_clear_one (AutomationLine& line, ::Selection& selection, Editing::CutCopyOp op)
{
using namespace Editing;
std::shared_ptr<Evoral::ControlList> what_we_got;
std::shared_ptr<AutomationList> alist (line.the_list());
XMLNode &before = alist->get_state();
/* convert time selection to automation list model coordinates */
timepos_t start = selection.time.front().start().earlier (line.get_origin());
timepos_t end = selection.time.front().end().earlier (line.get_origin());
std::cerr << "CCCO from " << start << " .. " << end << " based on " << selection.time.front().start() << " .. " << selection.time.front().end() << std::endl;
switch (op) {
case Delete:
if (alist->cut (start, end) != 0) {
_editing_context.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
}
break;
case Cut:
if ((what_we_got = alist->cut (start, end)) != 0) {
_editing_context.get_cut_buffer().add (what_we_got);
_editing_context.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
}
break;
case Copy:
if ((what_we_got = alist->copy (start, end)) != 0) {
_editing_context.get_cut_buffer().add (what_we_got);
}
break;
case Clear:
if ((what_we_got = alist->cut (start, end)) != 0) {
_editing_context.add_command(new MementoCommand<AutomationList>(*alist.get(), &before, &alist->get_state()));
}
break;
}
if (what_we_got) {
for (AutomationList::iterator x = what_we_got->begin(); x != what_we_got->end(); ++x) {
timepos_t when = (*x)->when;
double val = (*x)->value;
val = line.model_to_view_coord_y (val);
(*x)->when = when;
(*x)->value = val;
}
}
}

View File

@@ -90,6 +90,8 @@ class PianorollMidiView : public MidiView
void hide_overlay_text ();
void show_overlay_text ();
void cut_copy_clear (::Selection& selection, Editing::CutCopyOp);
protected:
bool scroll (GdkEventScroll* ev);
@@ -143,4 +145,7 @@ class PianorollMidiView : public MidiView
void reset_width_dependent_items (double pixel_width);
bool have_visible_automation () const;
void cut_copy_clear_one (AutomationLine& line, ::Selection& selection, Editing::CutCopyOp op);
void cut_copy_points (Editing::CutCopyOp op, Temporal::timepos_t const & earliest_time);
};