diff --git a/gtk2_ardour/automation_region_view.cc b/gtk2_ardour/automation_region_view.cc index 59bc1f6250..76591fa3d8 100644 --- a/gtk2_ardour/automation_region_view.cc +++ b/gtk2_ardour/automation_region_view.cc @@ -192,6 +192,34 @@ AutomationRegionView::add_automation_event (GdkEvent *, framepos_t when, double view->session()->set_dirty (); } +bool +AutomationRegionView::paste (framepos_t pos, + unsigned paste_count, + float times, + boost::shared_ptr slist) +{ + AutomationTimeAxisView* const view = automation_view(); + boost::shared_ptr my_list = _line->the_list(); + + if (view->session()->transport_rolling() && my_list->automation_write()) { + /* do not paste if this control is in write mode and we're rolling */ + return false; + } + + /* add multi-paste offset if applicable */ + pos += view->editor().get_paste_offset( + pos, paste_count, _line->time_converter().to(slist->length())); + + const double model_pos = _line->time_converter().from(pos - _line->time_converter().origin_b()); + + XMLNode& before = my_list->get_state(); + my_list->paste(*slist, model_pos, times); + view->session()->add_command( + new MementoCommand(*my_list.get(), &before, &my_list->get_state())); + + return true; +} + void AutomationRegionView::set_height (double h) { diff --git a/gtk2_ardour/automation_region_view.h b/gtk2_ardour/automation_region_view.h index 0bebf12a32..4e97e2f367 100644 --- a/gtk2_ardour/automation_region_view.h +++ b/gtk2_ardour/automation_region_view.h @@ -49,6 +49,11 @@ public: void init (bool wfd); + bool paste (framepos_t pos, + unsigned paste_count, + float times, + boost::shared_ptr slist); + inline AutomationTimeAxisView* automation_view() const { return dynamic_cast(&trackview); } diff --git a/gtk2_ardour/automation_selection.h b/gtk2_ardour/automation_selection.h index 6f30c588e2..204f4f19be 100644 --- a/gtk2_ardour/automation_selection.h +++ b/gtk2_ardour/automation_selection.h @@ -22,10 +22,23 @@ #include -namespace ARDOUR { - class AutomationList; -} +#include "ardour/automation_list.h" +#include "evoral/Parameter.hpp" -class AutomationSelection : public std::list > {}; +class AutomationSelection : public std::list > { +public: + const_iterator + get_nth(const Evoral::Parameter& param, size_t nth) const { + size_t count = 0; + for (const_iterator l = begin(); l != end(); ++l) { + if ((*l)->parameter() == param) { + if (count++ == nth) { + return l; + } + } + } + return end(); + } +}; #endif /* __ardour_gtk_automation_selection_h__ */ diff --git a/gtk2_ardour/automation_streamview.cc b/gtk2_ardour/automation_streamview.cc index 6dc766bdc5..5616cdebdb 100644 --- a/gtk2_ardour/automation_streamview.cc +++ b/gtk2_ardour/automation_streamview.cc @@ -16,8 +16,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include #include +#include +#include #include #include @@ -314,16 +315,16 @@ struct RegionPositionSorter { }; -/** @param pos Position, in session frames. - * @return AutomationLine to paste to for that position, or 0 if there is none appropriate. - */ -boost::shared_ptr -AutomationStreamView::paste_line (framepos_t pos) +bool +AutomationStreamView::paste (framepos_t pos, + unsigned paste_count, + float times, + boost::shared_ptr alist) { /* XXX: not sure how best to pick this; for now, just use the last region which starts before pos */ if (region_views.empty()) { - return boost::shared_ptr (); + return false; } region_views.sort (RegionPositionSorter ()); @@ -345,7 +346,5 @@ AutomationStreamView::paste_line (framepos_t pos) } AutomationRegionView* arv = dynamic_cast (*prev); - assert (arv); - - return arv->line (); + return arv ? arv->paste(pos, paste_count, times, alist) : false; } diff --git a/gtk2_ardour/automation_streamview.h b/gtk2_ardour/automation_streamview.h index d058f02434..082e3cc379 100644 --- a/gtk2_ardour/automation_streamview.h +++ b/gtk2_ardour/automation_streamview.h @@ -64,7 +64,11 @@ class AutomationStreamView : public StreamView void set_selected_points (PointSelection &); std::list > get_lines () const; - boost::shared_ptr paste_line (ARDOUR::framepos_t); + + bool paste (framepos_t pos, + unsigned paste_count, + float times, + boost::shared_ptr list); private: void setup_rec_box (); diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc index e0e9b9428f..61c5d28e19 100644 --- a/gtk2_ardour/automation_time_axis.cc +++ b/gtk2_ardour/automation_time_axis.cc @@ -48,6 +48,7 @@ #include "point_selection.h" #include "control_point.h" #include "utils.h" +#include "item_counts.h" #include "i18n.h" @@ -630,51 +631,43 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when, _session->set_dirty (); } -/** Paste a selection. - * @param pos Position to paste to (session frames). - * @param times Number of times to paste. - * @param selection Selection to paste. - * @param nth Index of the AutomationList within the selection to paste from. - */ bool -AutomationTimeAxisView::paste (framepos_t pos, unsigned paste_count, float times, Selection& selection, size_t nth) +AutomationTimeAxisView::paste (framepos_t pos, unsigned paste_count, float times, const Selection& selection, ItemCounts& counts) { - boost::shared_ptr line; - if (_line) { - line = _line; + return paste_one (pos, paste_count, times, selection, counts); } else if (_view) { - line = _view->paste_line (pos); + AutomationSelection::const_iterator l = selection.lines.get_nth(_parameter, counts.n_lines(_parameter)); + if (l != selection.lines.end() && _view->paste (pos, paste_count, times, *l)) { + counts.increase_n_lines(_parameter); + return true; + } } - if (!line) { - return false; - } - - return paste_one (*line, pos, paste_count, times, selection, nth); + return false; } bool -AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, unsigned paste_count, float times, Selection& selection, size_t nth) +AutomationTimeAxisView::paste_one (framepos_t pos, unsigned paste_count, float times, const Selection& selection, ItemCounts& counts) { - AutomationSelection::iterator p; - boost::shared_ptr alist(line.the_list()); + boost::shared_ptr alist(_line->the_list()); if (_session->transport_rolling() && alist->automation_write()) { /* do not paste if this control is in write mode and we're rolling */ return false; } - for (p = selection.lines.begin(); p != selection.lines.end() && nth; ++p, --nth) {} - + /* Get appropriate list from selection. */ + AutomationSelection::const_iterator p = selection.lines.get_nth(_parameter, counts.n_lines(_parameter)); if (p == selection.lines.end()) { return false; } + counts.increase_n_lines(_parameter); /* add multi-paste offset if applicable */ pos += _editor.get_paste_offset(pos, paste_count, (*p)->length()); - double const model_pos = line.time_converter().from (pos - line.time_converter().origin_b ()); + double const model_pos = _line->time_converter().from (pos - _line->time_converter().origin_b ()); XMLNode &before = alist->get_state(); alist->paste (**p, model_pos, times); diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h index 39a211a456..6db4bd4e64 100644 --- a/gtk2_ardour/automation_time_axis.h +++ b/gtk2_ardour/automation_time_axis.h @@ -51,7 +51,7 @@ class Selection; class Selectable; class AutomationStreamView; class AutomationController; - +class ItemCounts; class AutomationTimeAxisView : public TimeAxisView { public: @@ -93,7 +93,7 @@ class AutomationTimeAxisView : public TimeAxisView { /* editing operations */ void cut_copy_clear (Selection&, Editing::CutCopyOp); - bool paste (ARDOUR::framepos_t, unsigned paste_count, float times, Selection&, size_t nth); + bool paste (ARDOUR::framepos_t, unsigned paste_count, float times, const Selection&, ItemCounts&); int set_state (const XMLNode&, int version); @@ -171,7 +171,7 @@ class AutomationTimeAxisView : public TimeAxisView { void build_display_menu (); void cut_copy_clear_one (AutomationLine&, Selection&, Editing::CutCopyOp); - bool paste_one (AutomationLine&, ARDOUR::framepos_t, unsigned, float times, Selection&, size_t nth); + bool paste_one (ARDOUR::framepos_t, unsigned, float times, const Selection&, ItemCounts& counts); void route_going_away (); void set_automation_state (ARDOUR::AutoState); diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index e50ef98290..d6ab14dafc 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -3887,16 +3887,10 @@ Editor::get_paste_offset (framepos_t pos, unsigned paste_count, framecnt_t durat /* calculate basic unsnapped multi-paste offset */ framecnt_t offset = paste_count * duration; - bool success = true; - double snap_beats = get_grid_type_as_beats(success, pos); - if (success) { - /* we're snapped to something musical, round duration up */ - BeatsFramesConverter conv(_session->tempo_map(), pos); - const Evoral::MusicalTime dur_beats = conv.from(duration); - const framecnt_t snap_dur_beats = ceil(dur_beats / snap_beats) * snap_beats; - - offset = paste_count * conv.to(snap_dur_beats); - } + /* snap offset so pos + offset is aligned to the grid */ + framepos_t offset_pos = pos + offset; + snap_to(offset_pos, RoundUpMaybe); + offset = offset_pos - pos; return offset; } diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 5e0716d0dd..ed1cc7b256 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -63,6 +64,7 @@ #include "audio_region_view.h" #include "audio_streamview.h" #include "audio_time_axis.h" +#include "automation_region_view.h" #include "automation_time_axis.h" #include "control_point.h" #include "debug.h" @@ -75,6 +77,7 @@ #include "gui_thread.h" #include "insert_time_dialog.h" #include "interthread_progress_window.h" +#include "item_counts.h" #include "keyboard.h" #include "midi_region_view.h" #include "mixer_strip.h" @@ -3843,26 +3846,8 @@ Editor::cut_copy (CutCopyOp op) bool did_edit = false; - if (!selection->points.empty()) { - begin_reversible_command (opname + _(" points")); - did_edit = true; - cut_copy_points (op); - if (op == Cut || op == Delete) { - selection->clear_points (); - } - } else if (!selection->regions.empty() || !selection->points.empty()) { - - string thing_name; - - if (selection->regions.empty()) { - thing_name = _("points"); - } else if (selection->points.empty()) { - thing_name = _("regions"); - } else { - thing_name = _("objects"); - } - - begin_reversible_command (opname + ' ' + thing_name); + if (!selection->regions.empty() || !selection->points.empty()) { + begin_reversible_command (opname + ' ' + _("objects")); did_edit = true; if (!selection->regions.empty()) { @@ -3889,7 +3874,7 @@ Editor::cut_copy (CutCopyOp op) selection->set (start, end); } } else if (!selection->time.empty()) { - begin_reversible_command (opname + _(" range")); + begin_reversible_command (opname + ' ' + _("range")); did_edit = true; cut_copy_ranges (op); @@ -3912,10 +3897,11 @@ Editor::cut_copy (CutCopyOp op) } struct AutomationRecord { - AutomationRecord () : state (0) {} - AutomationRecord (XMLNode* s) : state (s) {} + AutomationRecord () : state (0) , line(NULL) {} + AutomationRecord (XMLNode* s, const AutomationLine* l) : state (s) , line (l) {} XMLNode* state; ///< state before any operation + const AutomationLine* line; ///< line this came from boost::shared_ptr copy; ///< copied events for the cut buffer }; @@ -3938,12 +3924,13 @@ Editor::cut_copy_points (CutCopyOp op) /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */ for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) { - boost::shared_ptr al = (*i)->line().the_list(); + const AutomationLine& line = (*i)->line(); + const boost::shared_ptr 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] = AutomationRecord (&al->get_state ()); + lists[al] = AutomationRecord (&al->get_state (), &line); } } @@ -3951,8 +3938,12 @@ Editor::cut_copy_points (CutCopyOp op) /* 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. */ + framepos_t start = std::numeric_limits::max(); for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) { i->second.copy = i->first->create (i->first->parameter ()); + + /* Calculate earliest start position of any point in selection. */ + start = std::min(start, i->second.line->session_position(i->first->begin())); } /* Add all selected points to the relevant copy ControlLists */ @@ -3962,11 +3953,18 @@ Editor::cut_copy_points (CutCopyOp op) lists[al].copy->fast_simple_add ((*j)->when, (*j)->value); } + /* Snap start time backwards, so copy/paste is snap aligned. */ + snap_to(start, RoundDownMaybe); + for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) { - /* Correct this copy list so that it starts at time 0 */ - double const start = i->second.copy->front()->when; + /* 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. */ + const AutomationLine* line = i->second.line; + const double line_offset = line->time_converter().from(start); + for (AutomationList::iterator j = i->second.copy->begin(); j != i->second.copy->end(); ++j) { - (*j)->when -= start; + (*j)->when -= line_offset; } /* And add it to the cut buffer */ @@ -4352,14 +4350,8 @@ Editor::paste_internal (framepos_t position, float times) { DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("apparent paste position is %1\n", position)); - if (internal_editing()) { - if (cut_buffer->midi_notes.empty()) { - return; - } - } else { - if (cut_buffer->empty()) { - return; - } + if (cut_buffer->empty(internal_editing())) { + return; } if (position == max_framepos) { @@ -4376,27 +4368,64 @@ Editor::paste_internal (framepos_t position, float times) last_paste_pos = position; } - TrackViewList ts; - TrackViewList::iterator i; - size_t nth; - /* get everything in the correct order */ - if (_edit_point == Editing::EditAtMouse && entered_track) { - /* With the mouse edit point, paste onto the track under the mouse */ - ts.push_back (entered_track); - } else if (_edit_point == Editing::EditAtMouse && entered_regionview) { - /* With the mouse edit point, paste onto the track of the region under the mouse */ - ts.push_back (&entered_regionview->get_time_axis_view()); - } else if (!selection->tracks.empty()) { - /* Otherwise, if there are some selected tracks, paste to them */ + TrackViewList ts; + if (!selection->tracks.empty()) { + /* If there is a track selection, paste into exactly those tracks and + only those tracks. This allows the user to be explicit and override + the below "do the reasonable thing" logic. */ ts = selection->tracks.filter_to_unique_playlists (); sort_track_selection (ts); - } else if (_last_cut_copy_source_track) { - /* Otherwise paste to the track that the cut/copy came from; - see discussion in mantis #3333. - */ - ts.push_back (_last_cut_copy_source_track); + } else { + /* Figure out which track to base the paste at. */ + TimeAxisView* base_track; + if (_edit_point == Editing::EditAtMouse && entered_track) { + /* With the mouse edit point, paste onto the track under the mouse. */ + base_track = entered_track; + } else if (_edit_point == Editing::EditAtMouse && entered_regionview) { + /* With the mouse edit point, paste onto the track of the region under the mouse. */ + base_track = &entered_regionview->get_time_axis_view(); + } else if (_last_cut_copy_source_track) { + /* Paste to the track that the cut/copy came from (see mantis #333). */ + base_track = _last_cut_copy_source_track; + } + + /* Walk up to parent if necessary, so base track is a route. */ + while (base_track->get_parent()) { + base_track = base_track->get_parent(); + } + + /* Add base track and all tracks below it. The paste logic will select + the appropriate object types from the cut buffer in relative order. */ + for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) { + if ((*i)->order() >= base_track->order()) { + ts.push_back(*i); + } + } + + /* Sort tracks so the nth track of type T will pick the nth object of type T. */ + sort_track_selection (ts); + + /* Add automation children of each track in order, for pasting several lines. */ + for (TrackViewList::iterator i = ts.begin(); i != ts.end();) { + /* Add any automation children for pasting several lines */ + RouteTimeAxisView* rtv = dynamic_cast(*i++); + if (!rtv) { + continue; + } + + typedef RouteTimeAxisView::AutomationTracks ATracks; + const ATracks& atracks = rtv->automation_tracks(); + for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) { + i = ts.insert(i, a->second.get()); + ++i; + } + } + + /* We now have a list of trackviews starting at base_track, including + automation children, in the order shown in the editor, e.g. R1, + R1.A1, R1.A2, R2, R2.A1, ... */ } if (internal_editing ()) { @@ -4424,8 +4453,9 @@ Editor::paste_internal (framepos_t position, float times) begin_reversible_command (Operations::paste); - for (nth = 0, i = ts.begin(); i != ts.end(); ++i, ++nth) { - (*i)->paste (position, paste_count, times, *cut_buffer, nth); + ItemCounts counts; + for (TrackViewList::iterator i = ts.begin(); i != ts.end(); ++i) { + (*i)->paste (position, paste_count, times, *cut_buffer, counts); } commit_reversible_command (); diff --git a/gtk2_ardour/item_counts.h b/gtk2_ardour/item_counts.h new file mode 100644 index 0000000000..b7c6dbd9c6 --- /dev/null +++ b/gtk2_ardour/item_counts.h @@ -0,0 +1,78 @@ +/* + Copyright (C) 2014 Paul Davis + Author: David Robillard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef __ardour_item_counts_h__ +#define __ardour_item_counts_h__ + +#include +#include +#include + +#include "ardour/data_type.h" +#include "evoral/Parameter.hpp" + +/** A count of various GUI items. + * + * This is used to keep track of 'consumption' of a selection when pasting, but + * may be useful elsewhere. + */ +class ItemCounts +{ +public: + 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); } + + void increase_n_playlists(ARDOUR::DataType t, size_t delta=1) { + increase_n(t, _playlists, delta); + } + + void increase_n_regions(ARDOUR::DataType t, size_t delta=1) { + increase_n(t, _regions, delta); + } + + void increase_n_lines(Evoral::Parameter t, size_t delta=1) { + increase_n(t, _lines, delta); + } + +private: + template + size_t + get_n(const Key& key, const typename std::map& counts) const { + typename std::map::const_iterator i = counts.find(key); + return (i == counts.end()) ? 0 : i->second; + } + + template + void + increase_n(const Key& key, typename std::map& counts, size_t delta) { + typename std::map::iterator i = counts.find(key); + if (i != counts.end()) { + i->second += delta; + } else { + counts.insert(std::make_pair(key, delta)); + } + } + + std::map _playlists; + std::map _regions; + std::map _lines; +}; + +#endif /* __ardour_item_counts_h__ */ diff --git a/gtk2_ardour/playlist_selection.h b/gtk2_ardour/playlist_selection.h index 4fcf1c64c8..93aea79093 100644 --- a/gtk2_ardour/playlist_selection.h +++ b/gtk2_ardour/playlist_selection.h @@ -27,6 +27,20 @@ namespace ARDOUR { class Playlist; } -struct PlaylistSelection : std::list > {}; +struct PlaylistSelection : std::list > { +public: + const_iterator + get_nth(ARDOUR::DataType type, size_t nth) const { + size_t count = 0; + for (const_iterator l = begin(); l != end(); ++l) { + if ((*l)->data_type() == type) { + if (count++ == nth) { + return l; + } + } + } + return end(); + } +}; #endif /* __ardour_gtk_playlist_selection_h__ */ diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index b447288566..1d89833b79 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -63,6 +63,7 @@ #include "automation_time_axis.h" #include "enums.h" #include "gui_thread.h" +#include "item_counts.h" #include "keyboard.h" #include "playlist_selector.h" #include "point_selection.h" @@ -1534,20 +1535,20 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op) } bool -RouteTimeAxisView::paste (framepos_t pos, unsigned paste_count, float times, Selection& selection, size_t nth) +RouteTimeAxisView::paste (framepos_t pos, unsigned paste_count, float times, const Selection& selection, ItemCounts& counts) { if (!is_track()) { return false; } - boost::shared_ptr pl = playlist (); - PlaylistSelection::iterator p; - - for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {} + boost::shared_ptr pl = playlist (); + const ARDOUR::DataType type = pl->data_type(); + PlaylistSelection::const_iterator p = selection.playlists.get_nth(type, counts.n_playlists(type)); if (p == selection.playlists.end()) { return false; } + counts.increase_n_playlists(type); DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("paste to %1\n", pos)); diff --git a/gtk2_ardour/route_time_axis.h b/gtk2_ardour/route_time_axis.h index 45b8afd82d..03060c702f 100644 --- a/gtk2_ardour/route_time_axis.h +++ b/gtk2_ardour/route_time_axis.h @@ -70,6 +70,7 @@ class AutomationLine; class ProcessorAutomationLine; class TimeSelection; class RouteGroupMenu; +class ItemCounts; class RouteTimeAxisView : public RouteUI, public TimeAxisView { @@ -99,7 +100,7 @@ public: /* Editing operations */ void cut_copy_clear (Selection&, Editing::CutCopyOp); - bool paste (ARDOUR::framepos_t, unsigned paste_count, float times, Selection&, size_t nth); + bool paste (ARDOUR::framepos_t, unsigned paste_count, float times, const Selection&, ItemCounts&); RegionView* combine_regions (); void uncombine_regions (); void uncombine_region (RegionView*); @@ -125,7 +126,7 @@ public: virtual void create_automation_child (const Evoral::Parameter& param, bool show) = 0; typedef std::map > AutomationTracks; - AutomationTracks automation_tracks() { return _automation_tracks; } + const AutomationTracks& automation_tracks() const { return _automation_tracks; } boost::shared_ptr automation_child(Evoral::Parameter param); virtual Gtk::CheckMenuItem* automation_child_menu_item (Evoral::Parameter); diff --git a/gtk2_ardour/selection.cc b/gtk2_ardour/selection.cc index 1bdc0fe8b0..abb49b7daf 100644 --- a/gtk2_ardour/selection.cc +++ b/gtk2_ardour/selection.cc @@ -951,7 +951,7 @@ Selection::empty (bool internal_selection) as a cut buffer. */ - return object_level_empty && midi_notes.empty(); + return object_level_empty && midi_notes.empty() && points.empty(); } void diff --git a/gtk2_ardour/time_axis_view.h b/gtk2_ardour/time_axis_view.h index 3dc440b54c..c46d23ae58 100644 --- a/gtk2_ardour/time_axis_view.h +++ b/gtk2_ardour/time_axis_view.h @@ -78,6 +78,7 @@ class RegionView; class GhostRegion; class StreamView; class ArdourDialog; +class ItemCounts; /** Abstract base class for time-axis views (horizontal editor 'strips') * @@ -165,7 +166,20 @@ class TimeAxisView : public virtual AxisView /* editing operations */ virtual void cut_copy_clear (Selection&, Editing::CutCopyOp) {} - virtual bool paste (ARDOUR::framepos_t, unsigned /*paste_count*/, float /*times*/, Selection&, size_t /*nth*/) { return false; } + + /** Paste a selection. + * @param pos Position to paste to (session frames). + * @param paste_count Number of pastes to the same location previously (multi-paste). + * @param times Number of times to paste. + * @param selection Selection to paste. + * @param counts Count of consumed selection items (used to find the + * correct item to paste here, then updated for the next pastee). + */ + virtual bool paste (ARDOUR::framepos_t pos, + unsigned paste_count, + float times, + const Selection& selection, + ItemCounts& counts) { return false; } virtual void set_selected_regionviews (RegionSelection&) {} virtual void set_selected_points (PointSelection&) {}