diff --git a/gtk2_ardour/editor.cc b/gtk2_ardour/editor.cc index f111aa18b1..87a70102a8 100644 --- a/gtk2_ardour/editor.cc +++ b/gtk2_ardour/editor.cc @@ -2904,12 +2904,6 @@ Editor::setup_toolbar () mouse_mode_box->pack_start (*mouse_mode_align, false, false); - edit_mode_strings.push_back (edit_mode_to_string (Slide)); - if (!Profile->get_sae()) { - edit_mode_strings.push_back (edit_mode_to_string (Splice)); - } - edit_mode_strings.push_back (edit_mode_to_string (Lock)); - edit_mode_selector.set_name ("mouse mode button"); edit_mode_selector.set_size_request (65, -1); edit_mode_selector.add_elements (ArdourButton::Inset); @@ -3126,6 +3120,7 @@ Editor::build_edit_mode_menu () edit_mode_selector.AddMenuElem (MenuElem ( edit_mode_to_string(Slide), sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Slide))); edit_mode_selector.AddMenuElem (MenuElem ( edit_mode_to_string(Splice), sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Splice))); + edit_mode_selector.AddMenuElem (MenuElem ( edit_mode_to_string(Ripple), sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Ripple))); edit_mode_selector.AddMenuElem (MenuElem ( edit_mode_to_string(Lock), sigc::bind (sigc::mem_fun(*this, &Editor::edit_mode_selection_done), (EditMode) Lock))); } @@ -3437,10 +3432,11 @@ Editor::cycle_edit_mode () if (Profile->get_sae()) { Config->set_edit_mode (Lock); } else { - Config->set_edit_mode (Splice); + Config->set_edit_mode (Ripple); } break; case Splice: + case Ripple: Config->set_edit_mode (Lock); break; case Lock: diff --git a/gtk2_ardour/editor.h b/gtk2_ardour/editor.h index 1398936979..e0c642b4fc 100644 --- a/gtk2_ardour/editor.h +++ b/gtk2_ardour/editor.h @@ -1613,7 +1613,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD void edit_mode_selection_done ( ARDOUR::EditMode m ); void build_edit_mode_menu (); Gtk::VBox edit_mode_box; - std::vector edit_mode_strings; void set_edit_mode (ARDOUR::EditMode); void cycle_edit_mode (); @@ -2092,6 +2091,7 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD friend class RegionDrag; friend class RegionMoveDrag; friend class RegionSpliceDrag; + friend class RegionRippleDrag; friend class TrimDrag; friend class MeterMarkerDrag; friend class TempoMarkerDrag; diff --git a/gtk2_ardour/editor_actions.cc b/gtk2_ardour/editor_actions.cc index d8889a9c81..1072b497c2 100644 --- a/gtk2_ardour/editor_actions.cc +++ b/gtk2_ardour/editor_actions.cc @@ -476,6 +476,7 @@ Editor::register_actions () ActionManager::register_action (editor_actions, "cycle-edit-point-with-marker", _("Change Edit Point Including Marker"), sigc::bind (sigc::mem_fun (*this, &Editor::cycle_edit_point), true)); if (!Profile->get_sae()) { ActionManager::register_action (editor_actions, "set-edit-splice", _("Splice"), sigc::bind (sigc::mem_fun (*this, &Editor::set_edit_mode), Splice)); + ActionManager::register_action (editor_actions, "set-edit-ripple", _("Ripple"), bind (mem_fun (*this, &Editor::set_edit_mode), Ripple)); } ActionManager::register_action (editor_actions, "set-edit-slide", _("Slide"), sigc::bind (sigc::mem_fun (*this, &Editor::set_edit_mode), Slide)); ActionManager::register_action (editor_actions, "set-edit-lock", _("Lock"), sigc::bind (sigc::mem_fun (*this, &Editor::set_edit_mode), Lock)); diff --git a/gtk2_ardour/editor_audio_import.cc b/gtk2_ardour/editor_audio_import.cc index da70df5c15..7a5f3982bf 100644 --- a/gtk2_ardour/editor_audio_import.cc +++ b/gtk2_ardour/editor_audio_import.cc @@ -888,6 +888,9 @@ Editor::finish_bringing_in_material (boost::shared_ptr region, uint32_t boost::shared_ptr copy (RegionFactory::create (region, region->properties())); playlist->clear_changes (); playlist->add_region (copy, pos); + if (Config->get_edit_mode() == Ripple) + playlist->ripple (pos, copy->length(), copy); + _session->add_command (new StatefulDiffCommand (playlist)); break; } diff --git a/gtk2_ardour/editor_drag.cc b/gtk2_ardour/editor_drag.cc index 191d111b0d..420fc52d31 100644 --- a/gtk2_ardour/editor_drag.cc +++ b/gtk2_ardour/editor_drag.cc @@ -599,7 +599,6 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r } } - _last_frame_position = *pending_region_position; } return dx; @@ -667,6 +666,7 @@ RegionMotionDrag::motion (GdkEvent* event, bool first_move) /* Work out the change in x */ framepos_t pending_region_position; double const x_delta = compute_x_delta (event, &pending_region_position); + _last_frame_position = pending_region_position; /* Work out the change in y */ @@ -1197,7 +1197,7 @@ RegionMoveDrag::remove_region_from_playlist ( playlist->clear_changes (); } - playlist->remove_region (region); + playlist->remove_region (region); // should be no need to ripple; we better already have rippled the playlist in RegionRippleDrag } @@ -1257,11 +1257,15 @@ RegionMoveDrag::collect_new_region_view (RegionView* rv) void RegionMoveDrag::add_stateful_diff_commands_for_playlists (PlaylistSet const & playlists) { + std::cerr << "RegionMoveDrag::add_stateful_diff_commands_for_playlists ()" << std::endl; for (PlaylistSet::const_iterator i = playlists.begin(); i != playlists.end(); ++i) { + std::cerr << "playlist: " << (*i)->name() << std::endl; StatefulDiffCommand* c = new StatefulDiffCommand (*i); if (!c->empty()) { + std::cerr << "added StatefulDiffCommand!" << std::endl; _editor->session()->add_command (c); } else { + std::cerr << "no StatefulDiffcommand to add..." << std::endl; delete c; } } @@ -1368,6 +1372,12 @@ RegionInsertDrag::finished (GdkEvent *, bool) _editor->begin_reversible_command (Operations::insert_region); playlist->clear_changes (); playlist->add_region (_primary->region (), _last_frame_position); + + // Mixbus doesn't seem to ripple when inserting regions from the list: should we? yes, probably + if (Config->get_edit_mode() == Ripple) { + playlist->ripple (_last_frame_position, _primary->region()->length(), _primary->region()); + } + _editor->session()->add_command (new StatefulDiffCommand (playlist)); _editor->commit_reversible_command (); @@ -1424,10 +1434,11 @@ RegionSpliceDrag::motion (GdkEvent* event, bool) dir = -1; } - RegionSelection copy (_editor->selection->regions); - - RegionSelectionByPosition cmp; - copy.sort (cmp); + // RegionSelection copy (_editor->selection->regions); + // RegionSelectionByPosition cmp; + // copy.sort (cmp); + RegionSelection copy; + _editor->selection->regions.by_position(copy); framepos_t const pf = adjusted_current_frame (event); @@ -1476,6 +1487,252 @@ RegionSpliceDrag::aborted (bool) /* XXX: TODO */ } +/*** + * ripple mode... + */ + +void +RegionRippleDrag::add_all_after_to_views(TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress) +{ + RegionSelection to_ripple; + TrackViewList tracks; + tracks.push_back (tav); // rv.get_time_axis_view ()); + + _editor->get_regions_after (to_ripple, where, tracks); + + for (RegionSelection::iterator i = to_ripple.begin(); i != to_ripple.end(); ++i) { + if (!exclude.contains (*i)) { + // the selection has already been added to _views + + if (drag_in_progress) { + (*i)->drag_start(); + ArdourCanvas::Group* rvg = (*i)->get_canvas_group(); + Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0)); + rvg->reparent (_editor->_region_motion_group); + (*i)->fake_set_opaque (true); + rvg->set_position (rv_canvas_offset); + } + _views.push_back (DraggingView (*i, this)); + } + } +} + +void +RegionRippleDrag::remove_unselected_from_views(framecnt_t amount) +{ + + std::cerr << "_views contains " << _views.size() << " views, including those on " << prev_tav->name() << std::endl; + + for (std::list::iterator i = _views.begin(); i != _views.end(); ) { + // we added all the regions after the selection + std::cerr << "iterating _views..." << std::endl; + std::cerr << "found " << i->view->region()->name() << " in _views..." << std::endl; + + std::list::iterator to_erase = i++; + if (!_editor->selection->regions.contains (to_erase->view)) { + std::cerr << "removing " << to_erase->view->region()->name() << " from _views..." << std::endl; + // restore the non-selected regions to their original playlist & positions, + // and then ripple them back by the length of the regions that were dragged away + // do the same things as RegionMotionDrag::aborted + + if (_item) { + _item->ungrab (); + } + + RegionView *rv = to_erase->view; + +#if 0 + // this is how RegionMotionDrag::aborted() does it... + TimeAxisView* tv = &(rv->get_time_axis_view ()); + RouteTimeAxisView* rtv = dynamic_cast (tv); + assert (rtv); + assert (rtv == prev_tav); + rv->get_canvas_group()->reparent (*rtv->view()->canvas_item()); +#else + // this should be equivalent... + rv->get_canvas_group()->reparent(prev_tav->view()->canvas_item()); +#endif + rv->get_canvas_group()->set_y_position (0); + rv->drag_end (); + rv->fake_set_opaque (false); + rv->move(-amount, 0); // XXX second parameter is y delta - do we need to do something? + // rv->set_height (rtv->view()->child_height ()); + + _views.erase (to_erase); + if (i == _views.end()) { + std::cerr << "reached end of _views iterator in loop!" << std::endl; + // break; + } + } + } +} + +RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list const & v) + : RegionMoveDrag (e, i, p, v, false, false) +{ + DEBUG_TRACE (DEBUG::Drags, "New RegionRippleDrag\n"); + // compute length of selection + RegionSelection selected_regions = _editor->selection->regions; + selection_length = selected_regions.end_frame() - selected_regions.start(); + + // we'll only allow dragging to another track in ripple mode if all the regions + // being dragged start off on the same track + allow_moves_across_tracks = (selected_regions.playlists().size() == 1); + prev_tav = NULL; + prev_amount = 0; + exclude = new RegionList; + for (RegionSelection::iterator i =selected_regions.begin(); i != selected_regions.end(); ++i) { + exclude->push_back((*i)->region()); + } + + // also add regions before start of selection to exclude, to be consistent with how Mixbus does ripple + RegionSelection copy; + selected_regions.by_position(copy); // get selected regions sorted by position into copy + + std::set > playlists = copy.playlists(); + std::set >::const_iterator pi; + + for (pi = playlists.begin(); pi != playlists.end(); ++pi) { + // find ripple start point on each applicable playlist + RegionView *first_selected_on_this_track = NULL; + for (RegionSelection::iterator i = copy.begin(); i != copy.end(); ++i) { + if ((*i)->region()->playlist() == (*pi)) { + // region is on this playlist - it's the first, because they're sorted + first_selected_on_this_track = *i; + break; + } + } + assert (first_selected_on_this_track); // we should always find the region in one of the playlists... + add_all_after_to_views ( + &first_selected_on_this_track->get_time_axis_view(), + first_selected_on_this_track->region()->position() + first_selected_on_this_track->region()->length(), + selected_regions, false); + } + + if (allow_moves_across_tracks) { + orig_tav = &(*selected_regions.begin())->get_time_axis_view(); + } else { + orig_tav = NULL; + } + +} + +void +RegionRippleDrag::motion (GdkEvent* event, bool first_move) +{ + /* Which trackview is this ? */ + + pair const tvp = _editor->trackview_by_y_position (_drags->current_pointer_y ()); + RouteTimeAxisView* tv = dynamic_cast (tvp.first); + + /* The region motion is only processed if the pointer is over + an audio track. + */ + + if (!tv || !tv->is_track()) { + /* To make sure we hide the verbose canvas cursor when the mouse is + not held over an audiotrack. + */ + _editor->verbose_cursor()->hide (); + return; + } + + framepos_t where = adjusted_current_frame (event); + assert (where >= 0); + framepos_t after; + double delta = compute_x_delta (event, &after); + + framecnt_t amount = _editor->pixel_to_sample (delta); + + if (allow_moves_across_tracks) { + // all the originally selected regions were on the same track + + framecnt_t adjust = 0; + if (prev_tav && tv != prev_tav) { + // dragged onto a different track + // remove the unselected regions from _views, restore them to their original positions + // and add the regions after the drop point on the new playlist to _views instead. + // undo the effect of rippling the previous playlist, and include the effect of removing + // the dragged region(s) from this track + + remove_unselected_from_views (prev_amount); + // ripple previous playlist according to the regions that have been removed onto the new playlist + prev_tav->playlist()->ripple(prev_position, -selection_length, exclude); + prev_amount = 0; + + // move just the selected regions + std::cerr << "calling RegionMoveDrag::motion() for single-track selection dragged across tracks, _views.size() now " << _views.size() << std::endl; + RegionMoveDrag::motion(event, first_move); + std::cerr << "RegionRippleDrag::motion() done!" << std::endl; + + // ensure that the ripple operation on the new playlist inserts selection_length time + adjust = selection_length; + // ripple the new current playlist + tv->playlist()->ripple (where, amount+adjust, exclude); + + // add regions after point where drag entered this track to subsequent ripples + add_all_after_to_views (tv, where, _editor->selection->regions, true); + std::cerr << "added regions on new track " << tv->name() << ", _views now contains " << _views.size() << " views" << std::endl; + + } else { + // motion on same track + // std::cerr << "calling RegionMoveDrag::motion() for single-track selection dragged within track..." << std::endl; + RegionMoveDrag::motion(event, first_move); + // std::cerr << "RegionRippleDrag::motion() done!" << std::endl; + + } + prev_tav = tv; + + // remember what we've done to this playlist so we can undo it if the selection is dragged to another track + prev_position = where; + if (!_x_constrained) { + prev_amount += amount; + } + } else { + // selection encompasses multiple tracks - just drag + // cross-track drags are forbidden + std::cerr << "calling RegionMoveDrag::motion() for multiple-track selection..." << std::endl; + RegionMoveDrag::motion(event, first_move); + std::cerr << "RegionRippleDrag::motion() done!" << std::endl; + + } + + _last_frame_position = after; +} + +void +RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred) +{ + if (!movement_occurred) { + return; + } + + _editor->begin_reversible_command(_("Ripple drag")); + + // if regions were dragged across tracks, we've rippled any later + // regions on the track the regions were dragged off, so we need + // to add the original track to the undo record + if (orig_tav) { + vector cmds; + orig_tav->playlist()->rdiff (cmds); + _editor->session()->add_commands (cmds); + } + + // other modified playlists are added to undo by RegionMoveDrag::finished() + RegionMoveDrag::finished (event, movement_occurred); + _editor->commit_reversible_command(); + +} + +void +RegionRippleDrag::aborted (bool movement_occurred) +{ + /* XXX: TODO */ + RegionMoveDrag::aborted (movement_occurred); + _views.clear (); +} + + RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v) : Drag (e, i), _view (dynamic_cast (v)) diff --git a/gtk2_ardour/editor_drag.h b/gtk2_ardour/editor_drag.h index 0bcfed9979..4b7114c67c 100644 --- a/gtk2_ardour/editor_drag.h +++ b/gtk2_ardour/editor_drag.h @@ -313,7 +313,7 @@ public: protected: double compute_x_delta (GdkEvent const *, ARDOUR::framepos_t *); - bool y_movement_allowed (int, double) const; + virtual bool y_movement_allowed (int, double) const; bool _brushing; ARDOUR::framepos_t _last_frame_position; ///< last position of the thing being dragged @@ -346,9 +346,11 @@ public: void setup_pointer_frame_offset (); -private: +protected: typedef std::set > PlaylistSet; + void add_stateful_diff_commands_for_playlists (PlaylistSet const &); +private: void finished_no_copy ( bool const, bool const, @@ -375,7 +377,6 @@ private: PlaylistSet& modified_playlists ); - void add_stateful_diff_commands_for_playlists (PlaylistSet const &); void collect_new_region_view (RegionView *); @@ -408,6 +409,42 @@ public: void aborted (bool); }; +/** Region drag in ripple mode */ + +class RegionRippleDrag : public RegionMoveDrag +{ +public: + RegionRippleDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list const &); + ~RegionRippleDrag () { delete exclude; } + + void motion (GdkEvent *, bool); + void finished (GdkEvent *, bool); + void aborted (bool); +protected: + bool y_movement_allowed (int delta_track, double delta_layer) const { + std::cerr << "RegionRippleDrag::y_movement_allowed (" << delta_track << ", " << delta_layer << ")..." << std::endl; + if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) { + if (delta_track) { + return allow_moves_across_tracks; + } else { + return true; + } + } + return false; + } +private: + TimeAxisView *prev_tav; // where regions were most recently dragged from + TimeAxisView *orig_tav; // where drag started + framecnt_t prev_amount; + framepos_t prev_position; + framecnt_t selection_length; + bool allow_moves_across_tracks; // only if all selected regions are on one track + ARDOUR::RegionList *exclude; + void add_all_after_to_views (TimeAxisView *tav, framepos_t where, const RegionSelection &exclude, bool drag_in_progress); + void remove_unselected_from_views (framecnt_t amount); + +}; + /** Drags to create regions */ class RegionCreateDrag : public Drag { diff --git a/gtk2_ardour/editor_mouse.cc b/gtk2_ardour/editor_mouse.cc index 928801d0b2..8e5a357ac4 100644 --- a/gtk2_ardour/editor_mouse.cc +++ b/gtk2_ardour/editor_mouse.cc @@ -2687,10 +2687,16 @@ Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region _region_motion_group->raise_to_top (); - if (Config->get_edit_mode() == Splice) { - _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer())); - } else { - _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false)); + switch (Config->get_edit_mode()) { + case Splice: + _drags->add (new RegionSpliceDrag (this, item, region_view, selection->regions.by_layer())); + break; + case Ripple: + _drags->add (new RegionRippleDrag (this, item, region_view, selection->regions.by_layer())); + break; + default: + _drags->add (new RegionMoveDrag (this, item, region_view, selection->regions.by_layer(), false, false)); + break; } } @@ -2717,7 +2723,7 @@ Editor::add_region_brush_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* return; } - if (Config->get_edit_mode() == Splice) { + if (Config->get_edit_mode() == Splice || Config->get_edit_mode() == Ripple) { return; } diff --git a/gtk2_ardour/editor_ops.cc b/gtk2_ardour/editor_ops.cc index 08d6297faa..30ca540535 100644 --- a/gtk2_ardour/editor_ops.cc +++ b/gtk2_ardour/editor_ops.cc @@ -2119,6 +2119,9 @@ Editor::insert_region_list_drag (boost::shared_ptr region, int x, int y) begin_reversible_command (_("insert dragged region")); playlist->clear_changes (); playlist->add_region (RegionFactory::create (region, true), where, 1.0); + if (Config->get_edit_mode() == Ripple) + playlist->ripple (where, region->length(), boost::shared_ptr()); + _session->add_command(new StatefulDiffCommand (playlist)); commit_reversible_command (); } @@ -2192,6 +2195,9 @@ Editor::insert_region_list_selection (float times) begin_reversible_command (_("insert region")); playlist->clear_changes (); playlist->add_region ((RegionFactory::create (region, true)), get_preferred_edit_position(), times); + if (Config->get_edit_mode() == Ripple) + playlist->ripple (get_preferred_edit_position(), region->length() * times, boost::shared_ptr()); + _session->add_command(new StatefulDiffCommand (playlist)); commit_reversible_command (); } @@ -4054,10 +4060,11 @@ Editor::remove_clicked_region () boost::shared_ptr playlist = clicked_routeview->playlist(); - begin_reversible_command (_("remove region")); playlist->clear_changes (); playlist->clear_owned_changes (); playlist->remove_region (clicked_regionview->region()); + if (Config->get_edit_mode() == Ripple) + playlist->ripple (clicked_regionview->region()->position(), -clicked_regionview->region()->length(), boost::shared_ptr()); /* We might have removed regions, which alters other regions' layering_index, so we need to do a recursive diff here. @@ -4120,6 +4127,9 @@ Editor::remove_selected_regions () playlist->clear_owned_changes (); playlist->freeze (); playlist->remove_region (*rl); + if (Config->get_edit_mode() == Ripple) + playlist->ripple ((*rl)->position(), -(*rl)->length(), boost::shared_ptr()); + } vector >::iterator pl; @@ -4249,12 +4259,16 @@ Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs) switch (op) { case Delete: pl->remove_region (r); + if (Config->get_edit_mode() == Ripple) + pl->ripple (r->position(), -r->length(), boost::shared_ptr()); break; case Cut: _xx = RegionFactory::create (r); npl->add_region (_xx, r->position() - first_position); pl->remove_region (r); + if (Config->get_edit_mode() == Ripple) + pl->ripple (r->position(), -r->length(), boost::shared_ptr()); break; case Copy: @@ -4263,7 +4277,9 @@ Editor::cut_copy_regions (CutCopyOp op, RegionSelection& rs) break; case Clear: - pl->remove_region (r); + pl->remove_region (r); + if (Config->get_edit_mode() == Ripple) + pl->ripple (r->position(), -r->length(), boost::shared_ptr()); break; } diff --git a/gtk2_ardour/route_time_axis.cc b/gtk2_ardour/route_time_axis.cc index 8eb4f58532..fd8f7f5995 100644 --- a/gtk2_ardour/route_time_axis.cc +++ b/gtk2_ardour/route_time_axis.cc @@ -1424,6 +1424,11 @@ RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, siz } pl->clear_changes (); + if (Config->get_edit_mode() == Ripple) { + std::pair extent = (*p)->get_extent(); + framecnt_t amount = extent.second - extent.first; + pl->ripple(pos, amount * times, boost::shared_ptr()); + } pl->paste (*p, pos, times); _session->add_command (new StatefulDiffCommand (pl)); diff --git a/libs/ardour/ardour/playlist.h b/libs/ardour/ardour/playlist.h index 5629a04629..ababa60063 100644 --- a/libs/ardour/ardour/playlist.h +++ b/libs/ardour/ardour/playlist.h @@ -144,6 +144,14 @@ public: void uncombine (boost::shared_ptr); void shuffle (boost::shared_ptr, int dir); + void ripple (framepos_t at, framecnt_t distance, RegionList *exclude); + void ripple (framepos_t at, framecnt_t distance, boost::shared_ptr exclude) { + RegionList el; + if (exclude) + el.push_back (exclude); + ripple (at, distance, &el); + } + void update_after_tempo_map_change (); boost::shared_ptr cut (std::list&, bool result_is_hidden = true); @@ -283,6 +291,7 @@ public: bool first_set_state; bool _hidden; bool _splicing; + bool _rippling; bool _shuffling; bool _nudging; uint32_t _refcnt; @@ -337,6 +346,11 @@ public: void splice_locked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude); void splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr exclude); + void core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude); + void ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude); + void ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude); + + virtual void remove_dependents (boost::shared_ptr /*region*/) {} virtual XMLNode& state (bool); diff --git a/libs/ardour/ardour/types.h b/libs/ardour/ardour/types.h index 1b9c3326c0..92a8c0da5b 100644 --- a/libs/ardour/ardour/types.h +++ b/libs/ardour/ardour/types.h @@ -341,6 +341,7 @@ namespace ARDOUR { enum EditMode { Slide, Splice, + Ripple, Lock }; diff --git a/libs/ardour/enums.cc b/libs/ardour/enums.cc index 948025cc2b..3f5ce75eb3 100644 --- a/libs/ardour/enums.cc +++ b/libs/ardour/enums.cc @@ -237,6 +237,7 @@ setup_enum_writer () REGISTER_ENUM (Slide); REGISTER_ENUM (Splice); + REGISTER_ENUM (Ripple); // XXX do the old enum values have to stay in order? REGISTER_ENUM (Lock); REGISTER (_EditMode); diff --git a/libs/ardour/playlist.cc b/libs/ardour/playlist.cc index d939ba61b0..11ca20972e 100644 --- a/libs/ardour/playlist.cc +++ b/libs/ardour/playlist.cc @@ -172,6 +172,7 @@ Playlist::Playlist (boost::shared_ptr other, string namestr, boo in_set_state--; _splicing = other->_splicing; + _rippling = other->_rippling; _nudging = other->_nudging; _edit_mode = other->_edit_mode; @@ -302,6 +303,7 @@ Playlist::init (bool hide) _refcnt = 0; _hidden = hide; _splicing = false; + _rippling = false; _shuffling = false; _nudging = false; in_set_state = 0; @@ -1399,7 +1401,7 @@ Playlist::flush_notifications (bool from_undo) if (_edit_mode == Splice) { splice_locked (at, distance, exclude); - } + } } void @@ -1456,12 +1458,63 @@ Playlist::flush_notifications (bool from_undo) _splicing = false; notify_contents_changed (); - } +} - void - Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr region) - { - if (in_set_state || _splicing || _nudging || _shuffling) { +void +Playlist::ripple_locked (framepos_t at, framecnt_t distance, RegionList *exclude) +{ + { + RegionWriteLock rl (this); + core_ripple (at, distance, exclude); + } +} + +void +Playlist::ripple_unlocked (framepos_t at, framecnt_t distance, RegionList *exclude) +{ + core_ripple (at, distance, exclude); +} + +void +Playlist::core_ripple (framepos_t at, framecnt_t distance, RegionList *exclude) +{ + if (distance == 0) { + return; + } + + _rippling = true; + RegionListProperty copy = regions; + for (RegionList::iterator i = copy.begin(); i != copy.end(); ++i) { + assert (i != copy.end()); + + if (exclude) { + if (std::find(exclude->begin(), exclude->end(), (*i)) != exclude->end()) { + continue; + } + } + + if ((*i)->position() >= at) { + framepos_t new_pos = (*i)->position() + distance; + framepos_t limit = max_framepos - (*i)->length(); + if (new_pos < 0) { + new_pos = 0; + } else if (new_pos >= limit ) { + new_pos = limit; + } + + (*i)->set_position (new_pos); + } + } + + _rippling = false; + notify_contents_changed (); +} + + +void +Playlist::region_bounds_changed (const PropertyChange& what_changed, boost::shared_ptr region) +{ + if (in_set_state || _splicing || _rippling || _nudging || _shuffling) { return; } @@ -2694,6 +2747,12 @@ Playlist::region_is_shuffle_constrained (boost::shared_ptr) return false; } +void +Playlist::ripple (framepos_t at, framecnt_t distance, RegionList *exclude) +{ + ripple_locked (at, distance, exclude); +} + void Playlist::update_after_tempo_map_change () { diff --git a/libs/ardour/utils.cc b/libs/ardour/utils.cc index d1d2372977..715c0d67dc 100644 --- a/libs/ardour/utils.cc +++ b/libs/ardour/utils.cc @@ -396,6 +396,8 @@ string_to_edit_mode (string str) return Splice; } else if (str == _("Slide")) { return Slide; + } else if (str == _("Ripple")) { + return Ripple; } else if (str == _("Lock")) { return Lock; } @@ -414,6 +416,9 @@ edit_mode_to_string (EditMode mode) case Lock: return _("Lock"); + case Ripple: + return _("Ripple"); + default: case Splice: return _("Splice");