* @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;
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());
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();
/* 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);
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);
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;
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)
{
/* 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);
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);
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;
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;
}
}
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 ();
/** 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 ();
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);
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;
}
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;
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();
/* 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*);
/* 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&) {}