summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtk2_ardour/automation_region_view.cc28
-rw-r--r--gtk2_ardour/automation_region_view.h5
-rw-r--r--gtk2_ardour/automation_selection.h23
-rw-r--r--gtk2_ardour/automation_streamview.cc19
-rw-r--r--gtk2_ardour/automation_streamview.h6
-rw-r--r--gtk2_ardour/automation_time_axis.cc37
-rw-r--r--gtk2_ardour/automation_time_axis.h6
-rw-r--r--gtk2_ardour/editor.cc14
-rw-r--r--gtk2_ardour/editor_ops.cc140
-rw-r--r--gtk2_ardour/item_counts.h78
-rw-r--r--gtk2_ardour/playlist_selection.h16
-rw-r--r--gtk2_ardour/route_time_axis.cc11
-rw-r--r--gtk2_ardour/route_time_axis.h5
-rw-r--r--gtk2_ardour/selection.cc2
-rw-r--r--gtk2_ardour/time_axis_view.h16
15 files changed, 290 insertions, 116 deletions
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<const ARDOUR::AutomationList> slist)
+{
+ AutomationTimeAxisView* const view = automation_view();
+ boost::shared_ptr<ARDOUR::AutomationList> 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<ARDOUR::AutomationList>(*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<const ARDOUR::AutomationList> slist);
+
inline AutomationTimeAxisView* automation_view() const
{ return dynamic_cast<AutomationTimeAxisView*>(&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 <list>
-namespace ARDOUR {
- class AutomationList;
-}
-
-class AutomationSelection : public std::list<boost::shared_ptr<ARDOUR::AutomationList> > {};
+#include "ardour/automation_list.h"
+#include "evoral/Parameter.hpp"
+
+class AutomationSelection : public std::list<boost::shared_ptr<ARDOUR::AutomationList> > {
+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 <cmath>
#include <cassert>
+#include <cmath>
+#include <list>
#include <utility>
#include <gtkmm.h>
@@ -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<AutomationLine>
-AutomationStreamView::paste_line (framepos_t pos)
+bool
+AutomationStreamView::paste (framepos_t pos,
+ unsigned paste_count,
+ float times,
+ boost::shared_ptr<ARDOUR::AutomationList> 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<AutomationLine> ();
+ return false;
}
region_views.sort (RegionPositionSorter ());
@@ -345,7 +346,5 @@ AutomationStreamView::paste_line (framepos_t pos)
}
AutomationRegionView* arv = dynamic_cast<AutomationRegionView*> (*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<boost::shared_ptr<AutomationLine> > get_lines () const;
- boost::shared_ptr<AutomationLine> paste_line (ARDOUR::framepos_t);
+
+ bool paste (framepos_t pos,
+ unsigned paste_count,
+ float times,
+ boost::shared_ptr<ARDOUR::AutomationList> 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<AutomationLine> line;
-
if (_line) {
- line = _line;
+ return paste_one (pos, paste_count, times, selection, counts);
} else if (_view) {
- line = _view->paste_line (pos);
- }
-
- if (!line) {
- return false;
+ 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;
+ }
}
- 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<AutomationList> alist(line.the_list());
+ boost::shared_ptr<AutomationList> 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 <cstdlib>
#include <cmath>
#include <string>
+#include <limits>
#include <map>
#include <set>
@@ -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<Evoral::ControlList> 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<AutomationList> al = (*i)->line().the_list();
+ const AutomationLine& line = (*i)->line();
+ const boost::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] = 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<framepos_t>::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<RouteTimeAxisView*>(*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 <cstddef>
+#include <map>
+#include <utility>
+
+#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<typename Key>
+ size_t
+ get_n(const Key& key, const typename std::map<Key, size_t>& counts) const {
+ typename std::map<Key, size_t>::const_iterator i = counts.find(key);
+ return (i == counts.end()) ? 0 : i->second;
+ }
+
+ template<typename Key>
+ void
+ increase_n(const Key& key, typename std::map<Key, size_t>& counts, size_t delta) {
+ typename std::map<Key, size_t>::iterator i = counts.find(key);
+ if (i != counts.end()) {
+ i->second += delta;
+ } else {
+ counts.insert(std::make_pair(key, delta));
+ }
+ }
+
+ std::map<ARDOUR::DataType, size_t> _playlists;
+ std::map<ARDOUR::DataType, size_t> _regions;
+ std::map<Evoral::Parameter, size_t> _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<boost::shared_ptr<ARDOUR::Playlist> > {};
+struct PlaylistSelection : std::list<boost::shared_ptr<ARDOUR::Playlist> > {
+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<Playlist> pl = playlist ();
- PlaylistSelection::iterator p;
-
- for (p = selection.playlists.begin(); p != selection.playlists.end() && nth; ++p, --nth) {}
+ boost::shared_ptr<Playlist> 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<Evoral::Parameter, boost::shared_ptr<AutomationTimeAxisView> > AutomationTracks;
- AutomationTracks automation_tracks() { return _automation_tracks; }
+ const AutomationTracks& automation_tracks() const { return _automation_tracks; }
boost::shared_ptr<AutomationTimeAxisView> 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&) {}