Merge branch 'ripple-mode-cc' into cairocanvas
authorColin Fletcher <colin.m.fletcher@googlemail.com>
Tue, 1 Jul 2014 18:10:47 +0000 (19:10 +0100)
committerColin Fletcher <colin.m.fletcher@googlemail.com>
Tue, 1 Jul 2014 18:10:47 +0000 (19:10 +0100)
Fix up merge conflicts in
gtk2_ardour/editor_mouse.cc
gtk2_ardour/editor_ops.cc

Also fix up compile errors.

14 files changed:
gtk2_ardour/editor.cc
gtk2_ardour/editor.h
gtk2_ardour/editor_actions.cc
gtk2_ardour/editor_audio_import.cc
gtk2_ardour/editor_drag.cc
gtk2_ardour/editor_drag.h
gtk2_ardour/editor_mouse.cc
gtk2_ardour/editor_ops.cc
gtk2_ardour/route_time_axis.cc
libs/ardour/ardour/playlist.h
libs/ardour/ardour/types.h
libs/ardour/enums.cc
libs/ardour/playlist.cc
libs/ardour/utils.cc

index a837a5a429a25db62cc5f6fa50eab3a131d66390..6f8db99da0ea00c1dca7f8acfa12de4ac48c92c2 100644 (file)
@@ -2825,12 +2825,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);
@@ -3047,6 +3041,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)));
 }
 
@@ -3358,10 +3353,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:
index 3013e3605110dffc1f81563291e48dd423b91f39..d6e38cee417d05298b01cb60964277e00be4a980 100644 (file)
@@ -1626,7 +1626,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<std::string> edit_mode_strings;
 
        void set_edit_mode (ARDOUR::EditMode);
        void cycle_edit_mode ();
@@ -2102,6 +2101,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;
index 1d19aca9c40e4823eaf0fc24601dd4614c434561..27fab388be8a9880910d756d8c2ceada9125c34e 100644 (file)
@@ -482,6 +482,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));
index 647abae1530fa3e0f759650ea1b370df8cc663bf..339249260e4610bb64d00f7b9400d900686b29a8 100644 (file)
@@ -893,6 +893,9 @@ Editor::finish_bringing_in_material (boost::shared_ptr<Region> region, uint32_t
                boost::shared_ptr<Region> 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;
        }
index c4000ddd45783c36cc5fba2b86c40dbc159b895e..9ad867bbcbbc8b425794341a045155f9c01ef325 100644 (file)
@@ -595,7 +595,6 @@ RegionMotionDrag::compute_x_delta (GdkEvent const * event, framepos_t* pending_r
                        }
                }
 
-               _last_frame_position = *pending_region_position;
        }
 
        return dx;
@@ -668,6 +667,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;
 
        /* Verify change in y */
        if (!y_movement_allowed (delta_time_axis_view, delta_layer)) {
@@ -1281,7 +1281,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
 }
 
 
@@ -1451,6 +1451,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 ();
 
@@ -1493,7 +1499,7 @@ RegionSpliceDrag::motion (GdkEvent* event, bool)
 
        if (!tv || !tv->is_track()) {
                /* To make sure we hide the verbose canvas cursor when the mouse is
-                  not held over and audiotrack.
+                  not held over an audio track.
                */
                _editor->verbose_cursor()->hide ();
                return;
@@ -1509,10 +1515,8 @@ RegionSpliceDrag::motion (GdkEvent* event, bool)
                dir = -1;
        }
 
-       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);
 
@@ -1561,6 +1565,289 @@ 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);
+       _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) {
+                               // do the same things that RegionMotionDrag::motion does when first_move
+                               // is true for the region views that we're adding to _views this time
+
+                               (*i)->drag_start();
+                               ArdourCanvas::Item* rvg = (*i)->get_canvas_group();
+                               Duple rv_canvas_offset = rvg->item_to_canvas (Duple (0,0));
+                               Duple dmg_canvas_offset = _editor->_drag_motion_group->canvas_origin ();
+                               rvg->reparent (_editor->_drag_motion_group);
+
+                               // XXX without the following, things jump in the y direction during drags
+                               // with it, they jump in the x direction
+                               // so we need to do the move in the y direction only
+                               // rvg->move (rv_canvas_offset - dmg_canvas_offset);
+                               std::cerr << "rv_canvas_offset = " << rv_canvas_offset << ", dmg_canvas_offset = " << dmg_canvas_offset << std::endl;
+                               Duple fudge = rv_canvas_offset - dmg_canvas_offset;
+                               fudge.x = 0;
+                               rvg->move (fudge);
+
+                       }
+                       _views.push_back (DraggingView (*i, this, tav));
+               }
+       }
+}
+
+void
+RegionRippleDrag::remove_unselected_from_views(framecnt_t amount, bool move_regions)
+{
+
+       for (std::list<DraggingView>::iterator i = _views.begin(); i != _views.end(); ) {
+               // we added all the regions after the selection 
+
+               std::list<DraggingView>::iterator to_erase = i++;
+               if (!_editor->selection->regions.contains (to_erase->view)) {
+                       // 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
+
+                       RegionView *rv = to_erase->view;
+                       TimeAxisView* tv = &(rv->get_time_axis_view ());
+                       RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (tv);
+                       assert (rtv);
+
+                       std::cerr << "rtv = " << rtv->name() << std::endl;
+
+                       // plonk them back onto their own track
+                       rv->get_canvas_group()->reparent(rtv->view()->canvas_item());
+                       rv->get_canvas_group()->set_y_position (0);
+                       rv->drag_end ();
+
+                       if (move_regions) {
+                               // move the underlying region to match the view
+                               rv->region()->set_position (rv->region()->position() + amount);
+                       } else {
+                               // restore the view to match the underlying region's original position
+                               rv->move(-amount, 0);   // second parameter is y delta - seems 0 is OK
+                       }
+
+                       rv->set_height (rtv->view()->child_height ());
+                       _views.erase (to_erase);
+               }
+       }
+}
+
+bool
+RegionRippleDrag::y_movement_allowed (int delta_track, double delta_layer) const
+{
+       if (RegionMotionDrag::y_movement_allowed (delta_track, delta_layer)) {
+               if (delta_track) {
+                       return allow_moves_across_tracks;
+               } else {
+                       return true;
+               }
+       }
+       return false;
+}
+
+RegionRippleDrag::RegionRippleDrag (Editor* e, ArdourCanvas::Item* i, RegionView* p, list<RegionView*> 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<boost::shared_ptr<ARDOUR::Playlist> > playlists = copy.playlists();
+       std::set<boost::shared_ptr<ARDOUR::Playlist> >::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<TimeAxisView*, double> const tvp = _editor->trackview_by_y_position (current_pointer_y ());
+       RouteTimeAxisView* tv = dynamic_cast<RouteTimeAxisView*> (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
+                       std::cerr << "dragged from " << prev_tav->name() << " to " << tv->name() << std::endl;
+
+                       remove_unselected_from_views (prev_amount, false);
+                       // 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
+                       RegionMoveDrag::motion(event, first_move); 
+
+                       // 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);
+
+               } else {
+                       // motion on same track
+                       RegionMoveDrag::motion(event, first_move); 
+               }
+               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;
+       } else {
+               // selection encompasses multiple tracks - just drag
+               // cross-track drags are forbidden
+               RegionMoveDrag::motion(event, first_move); 
+       }
+
+       if (!_x_constrained) {
+               prev_amount += amount;
+       }
+
+       _last_frame_position = after;
+}
+
+void
+RegionRippleDrag::finished (GdkEvent* event, bool movement_occurred)
+{
+       if (!movement_occurred) {
+               return;
+       }
+
+       _editor->begin_reversible_command(_("Ripple drag"));
+       
+       // remove the regions being rippled from the dragging view, updating them to 
+       // their new positions
+       remove_unselected_from_views (prev_amount, true);
+
+       if (allow_moves_across_tracks) {
+               if (orig_tav) {
+                       // 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
+                       std::cerr << "adding orig_tav " << orig_tav->name() << " to undo" << std::endl;
+                       orig_tav->playlist()->clear_changes();
+                       vector<Command*> cmds;
+                       orig_tav->playlist()->rdiff (cmds);
+                       _editor->session()->add_commands (cmds);
+               }
+               if (prev_tav && prev_tav != orig_tav) {
+                       std::cerr << "adding prev_tav " << prev_tav->name() << " to undo" << std::endl;
+                       prev_tav->playlist()->clear_changes();
+                       vector<Command*> cmds;
+                       prev_tav->playlist()->rdiff (cmds);
+                       _editor->session()->add_commands (cmds);
+               } else if (prev_tav) {
+                       std::cerr << "prev_tav == orig_tav" << std::endl;
+               }
+       } else {
+               // selection spanned multiple tracks - all will need adding to undo record
+
+               std::set<boost::shared_ptr<ARDOUR::Playlist> > playlists = _editor->selection->regions.playlists();
+               std::set<boost::shared_ptr<ARDOUR::Playlist> >::const_iterator pi;
+
+               for (pi = playlists.begin(); pi != playlists.end(); ++pi) {
+
+                       std::cerr << "adding playlist with selection " << (*pi)->name() << " to undo" << std::endl;
+                       (*pi)->clear_changes();
+                       vector<Command*> cmds;
+                       (*pi)->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)
+{
+       RegionMoveDrag::aborted (movement_occurred);
+       _views.clear ();
+}
+
+
 RegionCreateDrag::RegionCreateDrag (Editor* e, ArdourCanvas::Item* i, TimeAxisView* v)
        : Drag (e, i),
          _view (dynamic_cast<MidiTimeAxisView*> (v))
index 476d98a2d46b2c07d2794c0e3024c713e1e1bab5..d9eda5685a0b4f5ef95e296dad1aa645d6d93582 100644 (file)
@@ -316,7 +316,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
@@ -349,9 +349,11 @@ public:
 
        void setup_pointer_frame_offset ();
 
-private:
+protected:
        typedef std::set<boost::shared_ptr<ARDOUR::Playlist> > PlaylistSet;
+       void add_stateful_diff_commands_for_playlists (PlaylistSet const &);
 
+private:
        void finished_no_copy (
                bool const,
                bool const,
@@ -378,7 +380,6 @@ private:
                PlaylistSet& modified_playlists
                );
 
-       void add_stateful_diff_commands_for_playlists (PlaylistSet const &);
 
        void collect_new_region_view (RegionView *);
        RouteTimeAxisView* create_destination_time_axis (boost::shared_ptr<ARDOUR::Region>, TimeAxisView* original);
@@ -412,6 +413,33 @@ public:
        void aborted (bool);
 };
 
+/** Region drag in ripple mode */
+
+class RegionRippleDrag : public RegionMoveDrag
+{
+public:
+       RegionRippleDrag (Editor *, ArdourCanvas::Item *, RegionView *, std::list<RegionView*> 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;
+
+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, bool move_regions);
+
+};
+
 /** Drags to create regions */
 class RegionCreateDrag : public Drag
 {
index 634e383fa961c3cd6e6f8a869d6290ec954c1dd0..fffb67c46ddd28c5cab2728f0f17f848064f58fb 100644 (file)
@@ -2400,10 +2400,17 @@ Editor::add_region_drag (ArdourCanvas::Item* item, GdkEvent*, RegionView* region
                return;
        }
 
-       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;
+
        }
 }
 
@@ -2428,7 +2435,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;
        }
 
index 4882ac1fc39e836bba6d1ff25c328dfd7f2886a7..45568bdb3c9e1666608edf676c172548602ca5ef 100644 (file)
@@ -2110,6 +2110,8 @@ Editor::unhide_ranges ()
        }
 }
 
+/* INSERT/REPLACE */
+
 void
 Editor::insert_region_list_selection (float times)
 {
@@ -2142,6 +2144,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<Region>());
+
        _session->add_command(new StatefulDiffCommand (playlist));
        commit_reversible_command ();
 }
@@ -3726,7 +3731,7 @@ Editor::can_cut_copy () const
 
 
 /** Cut, copy or clear selected regions, automation points or a time range.
- * @param op Operation (Cut, Copy or Clear)
+ * @param op Operation (Delete, Cut, Copy or Clear)
  */
 void
 Editor::cut_copy (CutCopyOp op)
@@ -3762,7 +3767,7 @@ Editor::cut_copy (CutCopyOp op)
                }
        }
 
-       if ( op != Clear )  //"Delete" doesn't change copy/paste buf
+       if ( op != Delete )  //"Delete" doesn't change copy/paste buf
                cut_buffer->clear ();
 
        if (entered_marker) {
@@ -4002,10 +4007,11 @@ Editor::remove_clicked_region ()
 
        boost::shared_ptr<Playlist> 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<Region>());
 
        /* We might have removed regions, which alters other regions' layering_index,
           so we need to do a recursive diff here.
@@ -4068,6 +4074,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<Region>());
+
        }
 
        vector<boost::shared_ptr<Playlist> >::iterator pl;
@@ -4197,12 +4206,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<Region>());
                        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<Region>());
                        break;
 
                case Copy:
@@ -4211,7 +4224,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<Region>());
                        break;
                }
 
index e6a8f59be093fbf4299e5ae0783475d249a3a700..55dbec5f8bc0d186d958dd5360efe17e529d23a1 100644 (file)
@@ -1413,6 +1413,10 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
        switch (op) {
        case Delete:
                if (playlist->cut (time) != 0) {
+                       if (Config->get_edit_mode() == Ripple)
+                               playlist->ripple(time.start(), -time.length(), NULL);
+                               // no need to exclude any regions from rippling here
+
                         vector<Command*> cmds;
                         playlist->rdiff (cmds);
                         _session->add_commands (cmds);
@@ -1424,6 +1428,10 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
        case Cut:
                if ((what_we_got = playlist->cut (time)) != 0) {
                        _editor.get_cut_buffer().add (what_we_got);
+                       if (Config->get_edit_mode() == Ripple)
+                               playlist->ripple(time.start(), -time.length(), NULL);
+                               // no need to exclude any regions from rippling here
+
                         vector<Command*> cmds;
                         playlist->rdiff (cmds);
                         _session->add_commands (cmds);
@@ -1439,6 +1447,9 @@ RouteTimeAxisView::cut_copy_clear (Selection& selection, CutCopyOp op)
 
        case Clear:
                if ((what_we_got = playlist->cut (time)) != 0) {
+                       if (Config->get_edit_mode() == Ripple)
+                               playlist->ripple(time.start(), -time.length(), NULL);
+                               // no need to exclude any regions from rippling here
 
                         vector<Command*> cmds;
                         playlist->rdiff (cmds);
@@ -1473,8 +1484,18 @@ RouteTimeAxisView::paste (framepos_t pos, float times, Selection& selection, siz
                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("modified paste to %1\n", pos));
        }
 
-        pl->clear_changes ();
+       pl->clear_changes ();
+       if (Config->get_edit_mode() == Ripple) {
+               std::pair<framepos_t, framepos_t> extent = (*p)->get_extent();
+               framecnt_t amount = extent.second - extent.first;
+               pl->ripple(pos, amount * times, boost::shared_ptr<Region>());
+       }
        pl->paste (*p, pos, times);
+
+       vector<Command*> cmds;
+       pl->rdiff (cmds);
+       _session->add_commands (cmds);
+
        _session->add_command (new StatefulDiffCommand (pl));
 
        return true;
index 5629a0462968cd913697b4e0b10e8bb1ae43c28c..ababa600631d19f14e324b1e892d5dd8acb4ffa1 100644 (file)
@@ -144,6 +144,14 @@ public:
        void uncombine (boost::shared_ptr<Region>);
 
        void shuffle (boost::shared_ptr<Region>, int dir);
+       void ripple (framepos_t at, framecnt_t distance, RegionList *exclude);
+       void ripple (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> exclude) {
+                RegionList el;
+                if (exclude)
+                        el.push_back (exclude);
+                ripple (at, distance, &el);
+       }
+
        void update_after_tempo_map_change ();
 
        boost::shared_ptr<Playlist> cut  (std::list<AudioRange>&, 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<Region> exclude);
        void splice_unlocked (framepos_t at, framecnt_t distance, boost::shared_ptr<Region> 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> /*region*/) {}
 
        virtual XMLNode& state (bool);
index df7c40a66923c0b71de99ebd88af278011258eed..216de8bb0c31c6d5487c3fa4e5b40dc40acf2dab 100644 (file)
@@ -341,6 +341,7 @@ namespace ARDOUR {
        enum EditMode {
                Slide,
                Splice,
+               Ripple,
                Lock
        };
 
index 7a200ee0a037db6f3dd72f791695d5fb5dcb9a90..6f4158d5de6d42c0df87d9d7b2c2ed8828b3ef69 100644 (file)
@@ -235,6 +235,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);
 
index ef768cad96c7131bfcf1be9be65c26a6469e3d1c..266535da20ff15535007980bcc49a423068b2f5c 100644 (file)
@@ -172,6 +172,7 @@ Playlist::Playlist (boost::shared_ptr<const Playlist> 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;
@@ -706,7 +708,7 @@ Playlist::flush_notifications (bool from_undo)
                 }
         }
 
-        possibly_splice_unlocked (position, (pos + length) - position, boost::shared_ptr<Region>());
+        possibly_splice_unlocked (position, (pos + length) - position, region);
  }
 
  void
@@ -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> 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> region)
+{
+        if (in_set_state || _splicing || _rippling || _nudging || _shuffling) {
                 return;
         }
 
@@ -2694,6 +2747,12 @@ Playlist::region_is_shuffle_constrained (boost::shared_ptr<Region>)
        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 ()
 {
index 54943562cb906278ad9cb0475c62ff599597c16d..9c0aea885bba3d9213e2a1c8cdfc21731cd8e5bb 100644 (file)
@@ -423,6 +423,8 @@ ARDOUR::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;
        }
@@ -441,6 +443,9 @@ ARDOUR::edit_mode_to_string (EditMode mode)
        case Lock:
                return _("Lock");
 
+       case Ripple:
+               return _("Ripple");
+
        default:
        case Splice:
                return _("Splice");