summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2014-11-14 20:04:09 -0500
committerDavid Robillard <d@drobilla.net>2014-11-14 20:04:19 -0500
commit31acd96384b930de928aa8c7969336ba3401bbed (patch)
tree69137126cf3738fb242a7ff1d3875927c82f7541
parentb01d1813f88e303bae724d5bc6f51de0c2fb4e1b (diff)
Implement "multi-paste" for notes, regions, and automation.
The idea here is that pasting several times to the same location doesn't make sense. Instead, the paste is appended past the last paste, snapped to the grid. This make it simple to replicate a given section a number of times, simply by copying once and pasting several times. This behaviour only appears when successive pastes are done to the same location (whatever the edit point is). When the paste point changes, the "multi-paste" state is reset. Boots 'n cats 'n boots 'n cats.
-rw-r--r--gtk2_ardour/automation_time_axis.cc9
-rw-r--r--gtk2_ardour/automation_time_axis.h4
-rw-r--r--gtk2_ardour/editor.cc27
-rw-r--r--gtk2_ardour/editor.h6
-rw-r--r--gtk2_ardour/editor_ops.cc13
-rw-r--r--gtk2_ardour/midi_region_view.cc24
-rw-r--r--gtk2_ardour/midi_region_view.h2
-rw-r--r--gtk2_ardour/public_editor.h1
-rw-r--r--gtk2_ardour/route_time_axis.cc7
-rw-r--r--gtk2_ardour/route_time_axis.h2
-rw-r--r--gtk2_ardour/time_axis_view.h2
11 files changed, 80 insertions, 17 deletions
diff --git a/gtk2_ardour/automation_time_axis.cc b/gtk2_ardour/automation_time_axis.cc
index 2c342fb8c9..e0e9b9428f 100644
--- a/gtk2_ardour/automation_time_axis.cc
+++ b/gtk2_ardour/automation_time_axis.cc
@@ -637,7 +637,7 @@ AutomationTimeAxisView::add_automation_event (GdkEvent* event, framepos_t when,
* @param nth Index of the AutomationList within the selection to paste from.
*/
bool
-AutomationTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
+AutomationTimeAxisView::paste (framepos_t pos, unsigned paste_count, float times, Selection& selection, size_t nth)
{
boost::shared_ptr<AutomationLine> line;
@@ -651,11 +651,11 @@ AutomationTimeAxisView::paste (framepos_t pos, float times, Selection& selection
return false;
}
- return paste_one (*line, pos, times, selection, nth);
+ return paste_one (*line, pos, paste_count, times, selection, nth);
}
bool
-AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float times, Selection& selection, size_t nth)
+AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, unsigned paste_count, float times, Selection& selection, size_t nth)
{
AutomationSelection::iterator p;
boost::shared_ptr<AutomationList> alist(line.the_list());
@@ -671,6 +671,9 @@ AutomationTimeAxisView::paste_one (AutomationLine& line, framepos_t pos, float t
return false;
}
+ /* 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 ());
XMLNode &before = alist->get_state();
diff --git a/gtk2_ardour/automation_time_axis.h b/gtk2_ardour/automation_time_axis.h
index a468c12459..39a211a456 100644
--- a/gtk2_ardour/automation_time_axis.h
+++ b/gtk2_ardour/automation_time_axis.h
@@ -93,7 +93,7 @@ class AutomationTimeAxisView : public TimeAxisView {
/* editing operations */
void cut_copy_clear (Selection&, Editing::CutCopyOp);
- bool paste (ARDOUR::framepos_t, float times, Selection&, size_t nth);
+ bool paste (ARDOUR::framepos_t, unsigned paste_count, float times, Selection&, size_t nth);
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, float times, Selection&, size_t nth);
+ bool paste_one (AutomationLine&, ARDOUR::framepos_t, unsigned, float times, Selection&, size_t nth);
void route_going_away ();
void set_automation_state (ARDOUR::AutoState);
diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc
index d5c9aa6fea..a0dcf72db2 100644
--- a/gtk2_ardour/editor.cc
+++ b/gtk2_ardour/editor.cc
@@ -312,6 +312,8 @@ Editor::Editor ()
clicked_control_point = 0;
last_update_frame = 0;
pre_press_cursor = 0;
+ last_paste_pos = 0;
+ paste_count = 0;
_drags = new DragManager (this);
lock_dialog = 0;
ruler_dialog = 0;
@@ -3856,6 +3858,31 @@ Editor::playlist_selector () const
return *_playlist_selector;
}
+framecnt_t
+Editor::get_paste_offset (framepos_t pos, unsigned paste_count, framecnt_t duration)
+{
+ if (paste_count == 0) {
+ /* don't bother calculating an offset that will be zero anyway */
+ return 0;
+ }
+
+ /* 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);
+ }
+
+ return offset;
+}
+
Evoral::MusicalTime
Editor::get_grid_type_as_beats (bool& success, framepos_t position)
{
diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h
index 4dc5f80d6f..2831fc894f 100644
--- a/gtk2_ardour/editor.h
+++ b/gtk2_ardour/editor.h
@@ -313,6 +313,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
/* nudge is initiated by transport controls owned by ARDOUR_UI */
framecnt_t get_nudge_distance (framepos_t pos, framecnt_t& next);
+ framecnt_t get_paste_offset (framepos_t pos, unsigned paste_count, framecnt_t duration);
Evoral::MusicalTime get_grid_type_as_beats (bool& success, framepos_t position);
void nudge_forward (bool next, bool force_playhead);
@@ -1109,6 +1110,11 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
Gtkmm2ext::ActionMap editor_action_map;
Gtkmm2ext::Bindings key_bindings;
+ /* CUT/COPY/PASTE */
+
+ framepos_t last_paste_pos;
+ unsigned paste_count;
+
void cut_copy (Editing::CutCopyOp);
bool can_cut_copy () const;
void cut_copy_points (Editing::CutCopyOp);
diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc
index 98235e5861..f6ec077301 100644
--- a/gtk2_ardour/editor_ops.cc
+++ b/gtk2_ardour/editor_ops.cc
@@ -4364,6 +4364,15 @@ Editor::paste_internal (framepos_t position, float times)
DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
}
+ if (position == last_paste_pos) {
+ /* repeated paste in the same position */
+ ++paste_count;
+ } else {
+ /* paste in new location, reset repeated paste state */
+ paste_count = 0;
+ last_paste_pos = position;
+ }
+
TrackViewList ts;
TrackViewList::iterator i;
size_t nth;
@@ -4401,7 +4410,7 @@ Editor::paste_internal (framepos_t position, float times)
cb != cut_buffer->midi_notes.end() && r != rs.end(); ++r) {
MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (*r);
if (mrv) {
- mrv->paste (position, times, **cb);
+ mrv->paste (position, paste_count, times, **cb);
++cb;
}
}
@@ -4413,7 +4422,7 @@ 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, times, *cut_buffer, nth);
+ (*i)->paste (position, paste_count, times, *cut_buffer, nth);
}
commit_reversible_command ();
diff --git a/gtk2_ardour/midi_region_view.cc b/gtk2_ardour/midi_region_view.cc
index b560367c5c..fc8948e734 100644
--- a/gtk2_ardour/midi_region_view.cc
+++ b/gtk2_ardour/midi_region_view.cc
@@ -3321,25 +3321,37 @@ MidiRegionView::selection_as_cut_buffer () const
/** This method handles undo */
void
-MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
+MidiRegionView::paste (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
{
if (mcb.empty()) {
return;
}
+ PublicEditor& editor = trackview.editor ();
+
trackview.session()->begin_reversible_command (_("paste"));
start_note_diff_command (_("paste"));
- const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats (pos);
- const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
- const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
- Evoral::MusicalTime end_point = 0;
+ /* get snap duration, default to 1 beat if not snapped to anything musical */
+ bool success = true;
+ double snap_beats = editor.get_grid_type_as_beats(success, pos);
+ if (!success) {
+ snap_beats = 1.0;
+ }
+
+ const Evoral::MusicalTime first_time = (*mcb.notes().begin())->time();
+ const Evoral::MusicalTime last_time = (*mcb.notes().rbegin())->end_time();
+ const Evoral::MusicalTime duration = last_time - first_time;
+ const Evoral::MusicalTime snap_duration = ceil(duration / snap_beats) * snap_beats;
+ const Evoral::MusicalTime paste_offset = paste_count * snap_duration;
+ const Evoral::MusicalTime pos_beats = absolute_frames_to_source_beats(pos) + paste_offset;
+ Evoral::MusicalTime end_point = 0;
DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
first_time,
last_time,
- last_time - first_time, pos, _region->position(),
+ duration, pos, _region->position(),
pos_beats));
clear_selection ();
diff --git a/gtk2_ardour/midi_region_view.h b/gtk2_ardour/midi_region_view.h
index 65ca7df7ab..cda4d5802c 100644
--- a/gtk2_ardour/midi_region_view.h
+++ b/gtk2_ardour/midi_region_view.h
@@ -112,7 +112,7 @@ public:
void resolve_note(uint8_t note_num, double end_time);
void cut_copy_clear (Editing::CutCopyOp);
- void paste (framepos_t pos, float times, const MidiCutBuffer&);
+ void paste (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer&);
void add_canvas_patch_change (ARDOUR::MidiModel::PatchChangePtr patch, const std::string& displaytext, bool);
diff --git a/gtk2_ardour/public_editor.h b/gtk2_ardour/public_editor.h
index bd861ab085..4bf03bc72f 100644
--- a/gtk2_ardour/public_editor.h
+++ b/gtk2_ardour/public_editor.h
@@ -299,6 +299,7 @@ class PublicEditor : public Gtk::Window, public PBD::StatefulDestructible, publi
virtual void foreach_time_axis_view (sigc::slot<void,TimeAxisView&>) = 0;
virtual void add_to_idle_resize (TimeAxisView*, int32_t) = 0;
virtual framecnt_t get_nudge_distance (framepos_t pos, framecnt_t& next) = 0;
+ virtual framecnt_t get_paste_offset (framepos_t pos, unsigned paste_count, framecnt_t duration) = 0;
virtual Evoral::MusicalTime get_grid_type_as_beats (bool& success, framepos_t position) = 0;
virtual void edit_notes (TimeAxisViewItem&) = 0;
diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc
index c4d49e40b9..b447288566 100644
--- a/gtk2_ardour/route_time_axis.cc
+++ b/gtk2_ardour/route_time_axis.cc
@@ -1534,7 +1534,7 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
}
bool
-RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, size_t nth)
+RouteTimeAxisView::paste (framepos_t pos, unsigned paste_count, float times, Selection& selection, size_t nth)
{
if (!is_track()) {
return false;
@@ -1556,6 +1556,11 @@ RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, siz
DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
}
+ /* add multi-paste offset if applicable */
+ std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
+ const framecnt_t duration = extent.second - extent.first;
+ pos += _editor.get_paste_offset(pos, paste_count, duration);
+
pl->clear_changes ();
if (Config->get_edit_mode() == Ripple) {
std::pair<framepos_t, framepos_t> extent = (*p)->get_extent_with_endspace();
diff --git a/gtk2_ardour/route_time_axis.h b/gtk2_ardour/route_time_axis.h
index 8e0941d591..45b8afd82d 100644
--- a/gtk2_ardour/route_time_axis.h
+++ b/gtk2_ardour/route_time_axis.h
@@ -99,7 +99,7 @@ public:
/* Editing operations */
void cut_copy_clear (Selection&, Editing::CutCopyOp);
- bool paste (ARDOUR::framepos_t, float times, Selection&, size_t nth);
+ bool paste (ARDOUR::framepos_t, unsigned paste_count, float times, Selection&, size_t nth);
RegionView* combine_regions ();
void uncombine_regions ();
void uncombine_region (RegionView*);
diff --git a/gtk2_ardour/time_axis_view.h b/gtk2_ardour/time_axis_view.h
index cc7f7a0fe0..3dc440b54c 100644
--- a/gtk2_ardour/time_axis_view.h
+++ b/gtk2_ardour/time_axis_view.h
@@ -165,7 +165,7 @@ class TimeAxisView : public virtual AxisView
/* editing operations */
virtual void cut_copy_clear (Selection&, Editing::CutCopyOp) {}
- virtual bool paste (ARDOUR::framepos_t, float /*times*/, Selection&, size_t /*nth*/) { return false; }
+ virtual bool paste (ARDOUR::framepos_t, unsigned /*paste_count*/, float /*times*/, Selection&, size_t /*nth*/) { return false; }
virtual void set_selected_regionviews (RegionSelection&) {}
virtual void set_selected_points (PointSelection&) {}