add to LUA API (new Stripable API)
[ardour.git] / gtk2_ardour / editor_ops.cc
index 1005dc72b45668958b43dcab56cd488b56b85ae5..f7bd882260d309da105bba37430cb23c818d0745 100644 (file)
@@ -28,6 +28,8 @@
 #include <map>
 #include <set>
 
+#include <gtkmm/messagedialog.h>
+
 #include "pbd/error.h"
 #include "pbd/basename.h"
 #include "pbd/pthread_utils.h"
 #include "pbd/whitespace.h"
 #include "pbd/stateful_diff_command.h"
 
-#include <gtkmm2ext/utils.h>
-#include <gtkmm2ext/choice.h>
-#include <gtkmm2ext/popup.h>
+#include "gtkmm2ext/utils.h"
+
+#include "widgets/choice.h"
+#include "widgets/popup.h"
+#include "widgets/prompter.h"
 
 #include "ardour/audio_track.h"
 #include "ardour/audioregion.h"
@@ -59,6 +63,7 @@
 #include "ardour/strip_silence.h"
 #include "ardour/transient_detector.h"
 #include "ardour/transpose.h"
+#include "ardour/vca_manager.h"
 
 #include "canvas/canvas.h"
 
 #include "transpose_dialog.h"
 #include "transform_dialog.h"
 #include "ui_config.h"
+#include "vca_time_axis.h"
 
 #include "pbd/i18n.h"
 
@@ -110,6 +116,7 @@ using namespace ARDOUR;
 using namespace PBD;
 using namespace Gtk;
 using namespace Gtkmm2ext;
+using namespace ArdourWidgets;
 using namespace Editing;
 using Gtkmm2ext::Keyboard;
 
@@ -716,9 +723,12 @@ Editor::build_region_boundary_cache ()
                return;
        }
 
+       bool maybe_first_frame = false;
+
        switch (_snap_type) {
        case SnapToRegionStart:
                interesting_points.push_back (Start);
+               maybe_first_frame = true;
                break;
        case SnapToRegionEnd:
                interesting_points.push_back (End);
@@ -729,6 +739,7 @@ Editor::build_region_boundary_cache ()
        case SnapToRegionBoundary:
                interesting_points.push_back (Start);
                interesting_points.push_back (End);
+               maybe_first_frame = true;
                break;
        default:
                fatal << string_compose (_("build_region_boundary_cache called with snap_type = %1"), _snap_type) << endmsg;
@@ -745,6 +756,17 @@ Editor::build_region_boundary_cache ()
                tlist = track_views.filter_to_unique_playlists ();
        }
 
+       if (maybe_first_frame) {
+               TrackViewList::const_iterator i;
+               for (i = tlist.begin(); i != tlist.end(); ++i) {
+                       boost::shared_ptr<Playlist> pl = (*i)->playlist();
+                       if (pl && pl->count_regions_at (0)) {
+                               region_boundary_cache.push_back (0);
+                               break;
+                       }
+               }
+       }
+
        while (pos < _session->current_end_frame() && !at_end) {
 
                framepos_t rpos;
@@ -777,17 +799,6 @@ Editor::build_region_boundary_cache ()
                                break;
                        }
 
-                       float speed = 1.0f;
-                       RouteTimeAxisView *rtav;
-
-                       if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
-                               if (rtav->track() != 0) {
-                                       speed = rtav->track()->speed();
-                               }
-                       }
-
-                       rpos = track_frame_to_session_frame (rpos, speed);
-
                        if (rpos < lpos) {
                                lpos = rpos;
                        }
@@ -825,7 +836,6 @@ Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, Trac
        boost::shared_ptr<Region> ret;
        framepos_t rpos = 0;
 
-       float track_speed;
        framepos_t track_frame;
        RouteTimeAxisView *rtav;
 
@@ -834,13 +844,7 @@ Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, Trac
                framecnt_t distance;
                boost::shared_ptr<Region> r;
 
-               track_speed = 1.0f;
-               if ( (rtav = dynamic_cast<RouteTimeAxisView*>(*i)) != 0 ) {
-                       if (rtav->track()!=0)
-                               track_speed = rtav->track()->speed();
-               }
-
-               track_frame = session_frame_to_track_frame(frame, track_speed);
+               track_frame = frame;
 
                if ((r = (*i)->find_next_region (track_frame, point, dir)) == 0) {
                        continue;
@@ -860,9 +864,6 @@ Editor::find_next_region (framepos_t frame, RegionPoint point, int32_t dir, Trac
                        break;
                }
 
-               // rpos is a "track frame", converting it to "_session frame"
-               rpos = track_frame_to_session_frame(rpos, track_speed);
-
                if (rpos > frame) {
                        distance = rpos - frame;
                } else {
@@ -1030,17 +1031,6 @@ Editor::cursor_to_region_point (EditorCursor* cursor, RegionPoint point, int32_t
                break;
        }
 
-       float speed = 1.0f;
-       RouteTimeAxisView *rtav;
-
-       if ( ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0 ) {
-               if (rtav->track() != 0) {
-                       speed = rtav->track()->speed();
-               }
-       }
-
-       pos = track_frame_to_session_frame(pos, speed);
-
        if (cursor == playhead_cursor) {
                _session->request_locate (pos);
        } else {
@@ -1221,17 +1211,6 @@ Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
                break;
        }
 
-       float speed = 1.0f;
-       RouteTimeAxisView *rtav;
-
-       if (ontrack != 0 && (rtav = dynamic_cast<RouteTimeAxisView*>(ontrack)) != 0) {
-               if (rtav->track() != 0) {
-                       speed = rtav->track()->speed();
-               }
-       }
-
-       pos = track_frame_to_session_frame(pos, speed);
-
        loc->move_to (pos, 0);
 }
 
@@ -1767,6 +1746,13 @@ Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
                }
        }
 
+       //zoom-behavior-tweaks
+       //limit our maximum zoom to the session gui extents value
+       std::pair<framepos_t, framepos_t> ext = session_gui_extents();
+       framecnt_t session_extents_pp = ( ext.second - ext.first )  / _visible_canvas_width;
+       if (nspp > session_extents_pp)
+               nspp = session_extents_pp;
+
        temporal_zoom (nspp);
 }
 
@@ -2029,6 +2015,39 @@ Editor::temporal_zoom_session ()
        }
 }
 
+void
+Editor::temporal_zoom_extents ()
+{
+       ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_extents)
+
+       if (_session) {
+               std::pair<framepos_t, framepos_t> ext = session_gui_extents( false );  //in this case we want to zoom to the extents explicitly; ignore the users prefs for extra padding
+
+               framecnt_t start = ext.first;
+               framecnt_t end = ext.second;
+
+               if (_session->actively_recording () ) {
+                       framepos_t cur = playhead_cursor->current_frame ();
+                       if (cur > end) {
+                               /* recording beyond the end marker; zoom out
+                                * by 5 seconds more so that if 'follow
+                                * playhead' is active we don't immediately
+                                * scroll.
+                                */
+                               end = cur + _session->frame_rate() * 5;
+                       }
+               }
+
+               if ((start == 0 && end == 0) || end < start) {
+                       return;
+               }
+
+               calc_extra_zoom_edges(start, end);
+
+               temporal_zoom_by_frame (start, end);
+       }
+}
+
 void
 Editor::temporal_zoom_by_frame (framepos_t start, framepos_t end)
 {
@@ -2117,7 +2136,7 @@ Editor::choose_new_marker_name(string &name) {
                return true;
        }
 
-       ArdourPrompter dialog (true);
+       Prompter dialog (true);
 
        dialog.set_prompt (_("New Name:"));
 
@@ -3027,7 +3046,7 @@ Editor::split_multichannel_region ()
        vector< boost::shared_ptr<Region> > v;
 
        for (list<RegionView*>::iterator x = rs.begin(); x != rs.end(); ++x) {
-               (*x)->region()->separate_by_channel (*_session, v);
+               (*x)->region()->separate_by_channel (v);
        }
 }
 
@@ -3122,8 +3141,6 @@ Editor::separate_regions_between (const TimeSelection& ts)
 
                        /* XXX need to consider musical time selections here at some point */
 
-                       double speed = rtv->track()->speed();
-
                        for (list<AudioRange>::const_iterator t = ts.begin(); t != ts.end(); ++t) {
 
                                sigc::connection c = rtv->view()->RegionViewAdded.connect (
@@ -3131,16 +3148,15 @@ Editor::separate_regions_between (const TimeSelection& ts)
 
                                latest_regionviews.clear ();
 
-                               playlist->partition ((framepos_t)((*t).start * speed),
-                                                    (framepos_t)((*t).end * speed), false);
+                               playlist->partition ((*t).start, (*t).end, false);
 
                                c.disconnect ();
 
                                if (!latest_regionviews.empty()) {
 
                                        rtv->view()->foreach_regionview (sigc::bind (
-                                                                                sigc::ptr_fun (add_if_covered),
-                                                                                &(*t), &new_selection));
+                                                                                    sigc::ptr_fun (add_if_covered),
+                                                                                    &(*t), &new_selection));
 
                                        if (!in_command) {
                                                begin_reversible_command (_("separate"));
@@ -3274,10 +3290,10 @@ Editor::separate_under_selected_regions ()
 
                boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
 
-               if (!playlist) {
+               if (!playlist) {
                        // is this check necessary?
                        continue;
-               }
+               }
 
                vector<PlaylistState>::iterator i;
 
@@ -3293,7 +3309,7 @@ Editor::separate_under_selected_regions ()
                        PlaylistState before;
                        before.playlist = playlist;
                        before.before = &playlist->get_state();
-
+                       playlist->clear_changes ();
                        playlist->freeze ();
                        playlists.push_back(before);
                }
@@ -3320,15 +3336,22 @@ Editor::crop_region_to_selection ()
 {
        if (!selection->time.empty()) {
 
-               crop_region_to (selection->time.start(), selection->time.end_frame());
-
+               begin_reversible_command (_("Crop Regions to Time Selection"));
+               for (std::list<AudioRange>::iterator i = selection->time.begin(); i != selection->time.end(); ++i) {
+                       crop_region_to ((*i).start, (*i).end);
+               }
+               commit_reversible_command();
        } else {
 
                framepos_t start;
                framepos_t end;
 
                if (get_edit_op_range (start, end)) {
+                       begin_reversible_command (_("Crop Regions to Edit Range"));
+
                        crop_region_to (start, end);
+
+                       commit_reversible_command();
                }
        }
 
@@ -3375,7 +3398,6 @@ Editor::crop_region_to (framepos_t start, framepos_t end)
        framepos_t new_start;
        framepos_t new_end;
        framecnt_t new_length;
-       bool in_command = false;
 
        for (vector<boost::shared_ptr<Playlist> >::iterator i = playlists.begin(); i != playlists.end(); ++i) {
 
@@ -3405,19 +3427,11 @@ Editor::crop_region_to (framepos_t start, framepos_t end)
                        new_end = min (end, new_end);
                        new_length = new_end - new_start + 1;
 
-                       if(!in_command) {
-                               begin_reversible_command (_("trim to selection"));
-                               in_command = true;
-                       }
                        (*i)->clear_changes ();
                        (*i)->trim_to (new_start, new_length);
                        _session->add_command (new StatefulDiffCommand (*i));
                }
        }
-
-       if (in_command) {
-               commit_reversible_command ();
-       }
 }
 
 void
@@ -3779,16 +3793,11 @@ Editor::trim_region_to_location (const Location& loc, const char* str)
                        return;
                }
 
-               float speed = 1.0;
                framepos_t start;
                framepos_t end;
 
-               if (tav->track() != 0) {
-                       speed = tav->track()->speed();
-               }
-
-               start = session_frame_to_track_frame (loc.start(), speed);
-               end = session_frame_to_track_frame (loc.end(), speed);
+               start = loc.start();
+               end = loc.end();
 
                rv->region()->clear_changes ();
                rv->region()->trim_to (start, (end - start));
@@ -3839,13 +3848,6 @@ Editor::trim_to_region(bool forward)
                        continue;
                }
 
-               float speed = 1.0;
-
-               if (atav->track() != 0) {
-                       speed = atav->track()->speed();
-               }
-
-
                boost::shared_ptr<Region> region = arv->region();
                boost::shared_ptr<Playlist> playlist (region->playlist());
 
@@ -3853,26 +3855,25 @@ Editor::trim_to_region(bool forward)
 
                if (forward) {
 
-                   next_region = playlist->find_next_region (region->first_frame(), Start, 1);
+                       next_region = playlist->find_next_region (region->first_frame(), Start, 1);
 
-                   if (!next_region) {
-                       continue;
-                   }
+                       if (!next_region) {
+                               continue;
+                       }
 
-                   region->trim_end((framepos_t) ( (next_region->first_frame() - 1) * speed));
+                   region->trim_end (next_region->first_frame() - 1);
                    arv->region_changed (PropertyChange (ARDOUR::Properties::length));
                }
                else {
 
-                   next_region = playlist->find_next_region (region->first_frame(), Start, 0);
-
-                   if(!next_region){
-                       continue;
-                   }
+                       next_region = playlist->find_next_region (region->first_frame(), Start, 0);
 
-                   region->trim_front((framepos_t) ((next_region->last_frame() + 1) * speed));
+                       if (!next_region) {
+                               continue;
+                       }
 
-                   arv->region_changed (ARDOUR::bounds_change);
+                       region->trim_front (next_region->last_frame() + 1);
+                       arv->region_changed (ARDOUR::bounds_change);
                }
 
                if (!in_command) {
@@ -3945,8 +3946,8 @@ Editor::freeze_route ()
 
        if (clicked_routeview->track()->has_external_redirects()) {
                MessageDialog d (string_compose (_("<b>%1</b>\n\nThis track has at least one send/insert/return as part of its signal flow.\n\n"
-                                                  "Freezing will only process the signal as far as the first send/insert/return."),
-                                                clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
+                                                  "Freezing will only process the signal as far as the first send/insert/return."),
+                                                clicked_routeview->track()->name()), true, MESSAGE_INFO, BUTTONS_NONE, true);
 
                d.add_button (_("Freeze anyway"), Gtk::RESPONSE_OK);
                d.add_button (_("Don't freeze"), Gtk::RESPONSE_CANCEL);
@@ -4143,8 +4144,9 @@ Editor::cut_copy (CutCopyOp op)
                }
        }
 
-       if ( op != Delete )  //"Delete" doesn't change copy/paste buf
+       if ( op != Delete ) { //"Delete" doesn't change copy/paste buf
                cut_buffer->clear ();
+       }
 
        if (entered_marker) {
 
@@ -4216,7 +4218,7 @@ Editor::cut_copy (CutCopyOp op)
        if (did_edit) {
                /* reset repeated paste state */
                paste_count    = 0;
-               last_paste_pos = 0;
+               last_paste_pos = -1;
                commit_reversible_command ();
        }
 
@@ -4463,10 +4465,10 @@ Editor::remove_selected_regions ()
 
                boost::shared_ptr<Playlist> playlist = (*rl)->playlist();
 
-               if (!playlist) {
+               if (!playlist) {
                        // is this check necessary?
                        continue;
-               }
+               }
 
                /* get_regions_from_selection_and_entered() guarantees that
                   the playlists involved are unique, so there is no need
@@ -4823,8 +4825,8 @@ Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
            /* Only one line copied, and one automation track selected.  Do a
               "greedy" paste from one automation type to another. */
 
-           PasteContext ctx(paste_count, times, ItemCounts(), true);
-           ts.front()->paste (position, *cut_buffer, ctx, sub_num);
+               PasteContext ctx(paste_count, times, ItemCounts(), true);
+               ts.front()->paste (position, *cut_buffer, ctx, sub_num);
 
        } else {
 
@@ -4949,7 +4951,7 @@ Editor::reset_point_selection ()
 {
        for (PointSelection::iterator i = selection->points.begin(); i != selection->points.end(); ++i) {
                ARDOUR::AutomationList::iterator j = (*i)->model ();
-               (*j)->value = (*i)->line().the_list()->default_value ();
+               (*j)->value = (*i)->line().the_list()->descriptor ().normal;
        }
 }
 
@@ -5041,12 +5043,12 @@ Editor::remove_last_capture ()
 
        if (Config->get_verify_remove_last_capture()) {
                prompt  = _("Do you really want to destroy the last capture?"
-                           "\n(This is destructive and cannot be undone)");
+                           "\n(This is destructive and cannot be undone)");
 
                choices.push_back (_("No, do nothing."));
                choices.push_back (_("Yes, destroy it."));
 
-               Gtkmm2ext::Choice prompter (_("Destroy last capture"), prompt, choices);
+               Choice prompter (_("Destroy last capture"), prompt, choices);
 
                if (prompter.run () == 1) {
                        _session->remove_last_capture ();
@@ -5296,7 +5298,7 @@ Editor::strip_region_silence ()
 
        for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
                AudioRegionView* const arv = dynamic_cast<AudioRegionView*> (*i);
-               if (arv) {
+               if (arv) {
                        audio_only.push_back (arv);
                }
        }
@@ -5363,6 +5365,7 @@ Editor::apply_midi_note_edit_op (MidiOperator& op, const RegionSelection& rs)
 
        if (in_command) {
                commit_reversible_command ();
+               _session->set_dirty ();
        }
 }
 
@@ -5869,18 +5872,18 @@ Editor::toggle_solo ()
        boost::shared_ptr<ControlList> cl (new ControlList);
 
        for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
-               RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
+               StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
 
-               if (!rtav) {
+               if (!stav || !stav->stripable()->solo_control()) {
                        continue;
                }
 
                if (first) {
-                       new_state = !rtav->route()->soloed ();
+                       new_state = !stav->stripable()->solo_control()->soloed ();
                        first = false;
                }
 
-               cl->push_back (rtav->route()->solo_control());
+               cl->push_back (stav->stripable()->solo_control());
        }
 
        _session->set_controls (cl, new_state ? 1.0 : 0.0, Controllable::UseGroup);
@@ -5891,24 +5894,24 @@ Editor::toggle_mute ()
 {
        bool new_state = false;
        bool first = true;
-       boost::shared_ptr<RouteList> rl (new RouteList);
+       boost::shared_ptr<ControlList> cl (new ControlList);
 
        for (TrackSelection::iterator i = selection->tracks.begin(); i != selection->tracks.end(); ++i) {
-               RouteTimeAxisView *rtav = dynamic_cast<RouteTimeAxisView *>(*i);
+               StripableTimeAxisView *stav = dynamic_cast<StripableTimeAxisView *>(*i);
 
-               if (!rtav) {
+               if (!stav || !stav->stripable()->mute_control()) {
                        continue;
                }
 
                if (first) {
-                       new_state = !rtav->route()->muted();
+                       new_state = !stav->stripable()->mute_control()->muted();
                        first = false;
                }
 
-               rl->push_back (rtav->route());
+               cl->push_back (stav->stripable()->mute_control());
        }
 
-       _session->set_controls (route_list_to_control_list (rl, &Stripable::mute_control), new_state, Controllable::UseGroup);
+       _session->set_controls (cl, new_state, Controllable::UseGroup);
 }
 
 void
@@ -6312,7 +6315,7 @@ Editor::split_region ()
 }
 
 void
-Editor::select_next_route()
+Editor::select_next_stripable (bool routes_only)
 {
        if (selection->tracks.empty()) {
                selection->set (track_views.front());
@@ -6321,7 +6324,7 @@ Editor::select_next_route()
 
        TimeAxisView* current = selection->tracks.front();
 
-       RouteUI *rui;
+       bool valid;
        do {
                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
 
@@ -6337,9 +6340,14 @@ Editor::select_next_route()
                        }
                }
 
-               rui = dynamic_cast<RouteUI *>(current);
+               if (routes_only) {
+                       RouteUI* rui = dynamic_cast<RouteUI *>(current);
+                       valid = rui && rui->route()->active();
+               } else {
+                       valid = 0 != current->stripable ().get();
+               }
 
-       } while (current->hidden() || (rui == NULL) || !rui->route()->active());
+       } while (current->hidden() || !valid);
 
        selection->set (current);
 
@@ -6347,7 +6355,7 @@ Editor::select_next_route()
 }
 
 void
-Editor::select_prev_route()
+Editor::select_prev_stripable (bool routes_only)
 {
        if (selection->tracks.empty()) {
                selection->set (track_views.front());
@@ -6356,7 +6364,7 @@ Editor::select_prev_route()
 
        TimeAxisView* current = selection->tracks.front();
 
-       RouteUI *rui;
+       bool valid;
        do {
                for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
 
@@ -6370,9 +6378,14 @@ Editor::select_prev_route()
                                break;
                        }
                }
-               rui = dynamic_cast<RouteUI *>(current);
+               if (routes_only) {
+                       RouteUI* rui = dynamic_cast<RouteUI *>(current);
+                       valid = rui && rui->route()->active();
+               } else {
+                       valid = 0 != current->stripable ().get();
+               }
 
-       } while (current->hidden() || (rui == NULL) || !rui->route()->active());
+       } while (current->hidden() || !valid);
 
        selection->set (current);
 
@@ -7254,7 +7267,7 @@ Editor::playhead_forward_to_grid ()
 
        if (pos.frame < max_framepos - 1) {
                pos.frame += 2;
-               snap_to_internal (pos, RoundUpAlways, false);
+               snap_to_internal (pos, RoundUpAlways, false, true);
                _session->request_locate (pos.frame);
        }
 }
@@ -7271,7 +7284,7 @@ Editor::playhead_backward_to_grid ()
 
        if (pos.frame > 2) {
                pos.frame -= 2;
-               snap_to_internal (pos, RoundDownAlways, false);
+               snap_to_internal (pos, RoundDownAlways, false, true);
                _session->request_locate (pos.frame);
        }
 }
@@ -7341,20 +7354,29 @@ Editor::_remove_tracks ()
        string prompt;
        int ntracks = 0;
        int nbusses = 0;
+       int nvcas = 0;
        const char* trackstr;
        const char* busstr;
+       const char* vcastr;
        vector<boost::shared_ptr<Route> > routes;
+       vector<boost::shared_ptr<VCA> > vcas;
        bool special_bus = false;
 
        for (TrackSelection::iterator x = ts.begin(); x != ts.end(); ++x) {
+               VCATimeAxisView* vtv = dynamic_cast<VCATimeAxisView*> (*x);
+               if (vtv) {
+                       vcas.push_back (vtv->vca());
+                       ++nvcas;
+                       continue;
+               }
                RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> (*x);
                if (!rtv) {
                        continue;
                }
                if (rtv->is_track()) {
-                       ntracks++;
+                       ++ntracks;
                } else {
-                       nbusses++;
+                       ++nbusses;
                }
                routes.push_back (rtv->_route);
 
@@ -7381,45 +7403,68 @@ edit your ardour.rc file to set the\n\
                return;
        }
 
-       if (ntracks + nbusses == 0) {
+       if (ntracks + nbusses + nvcas == 0) {
                return;
        }
 
+       string title;
+
        trackstr = P_("track", "tracks", ntracks);
        busstr = P_("bus", "busses", nbusses);
+       vcastr = P_("VCA", "VCAs", nvcas);
 
-       if (ntracks) {
-               if (nbusses) {
-                       prompt  = string_compose (_("Do you really want to remove %1 %2 and %3 %4?\n"
-                                                   "(You may also lose the playlists associated with the %2)\n\n"
-                                                   "This action cannot be undone, and the session file will be overwritten!"),
-                                                 ntracks, trackstr, nbusses, busstr);
-               } else {
-                       prompt  = string_compose (_("Do you really want to remove %1 %2?\n"
-                                                   "(You may also lose the playlists associated with the %2)\n\n"
-                                                   "This action cannot be undone, and the session file will be overwritten!"),
-                                                 ntracks, trackstr);
-               }
-       } else if (nbusses) {
-               prompt  = string_compose (_("Do you really want to remove %1 %2?\n\n"
-                                           "This action cannot be undone, and the session file will be overwritten"),
-                                         nbusses, busstr);
+       if (ntracks > 0 && nbusses > 0 && nvcas > 0) {
+               title = _("Remove various strips");
+               prompt = string_compose (_("Do you really want to remove %1 %2, %3 %4 and %5 %6?"),
+                                                 ntracks, trackstr, nbusses, busstr, nvcas, vcastr);
+       }
+       else if (ntracks > 0 && nbusses > 0) {
+               title = string_compose (_("Remove %1 and %2"), trackstr, busstr);
+               prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
+                               ntracks, trackstr, nbusses, busstr);
+       }
+       else if (ntracks > 0 && nvcas > 0) {
+               title = string_compose (_("Remove %1 and %2"), trackstr, vcastr);
+               prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
+                               ntracks, trackstr, nvcas, vcastr);
        }
+       else if (nbusses > 0 && nvcas > 0) {
+               title = string_compose (_("Remove %1 and %2"), busstr, vcastr);
+               prompt = string_compose (_("Do you really want to remove %1 %2 and %3 %4?"),
+                               nbusses, busstr, nvcas, vcastr);
+       }
+       else if (ntracks > 0) {
+               title = string_compose (_("Remove %1"), trackstr);
+               prompt  = string_compose (_("Do you really want to remove %1 %2?"),
+                               ntracks, trackstr);
+       }
+       else if (nbusses > 0) {
+               title = string_compose (_("Remove %1"), busstr);
+               prompt  = string_compose (_("Do you really want to remove %1 %2?"),
+                               nbusses, busstr);
+       }
+       else if (nvcas > 0) {
+               title = string_compose (_("Remove %1"), vcastr);
+               prompt  = string_compose (_("Do you really want to remove %1 %2?"),
+                               nvcas, vcastr);
+       }
+       else {
+               assert (0);
+       }
+
+       if (ntracks > 0) {
+                       prompt += "\n" + string_compose ("(You may also lose the playlists associated with the %1)", trackstr) + "\n";
+       }
+
+       prompt += "\n" + string(_("This action cannot be undone, and the session file will be overwritten!"));
 
        choices.push_back (_("No, do nothing."));
-       if (ntracks + nbusses > 1) {
+       if (ntracks + nbusses + nvcas > 1) {
                choices.push_back (_("Yes, remove them."));
        } else {
                choices.push_back (_("Yes, remove it."));
        }
 
-       string title;
-       if (ntracks) {
-               title = string_compose (_("Remove %1"), trackstr);
-       } else {
-               title = string_compose (_("Remove %1"), busstr);
-       }
-
        Choice prompter (title, prompt, choices);
 
        if (prompter.run () != 1) {
@@ -7453,6 +7498,11 @@ edit your ardour.rc file to set the\n\
                        rl->push_back (*x);
                }
                _session->remove_routes (rl);
+
+               for (vector<boost::shared_ptr<VCA> >::iterator x = vcas.begin(); x != vcas.end(); ++x) {
+                       _session->vca_manager().remove_vca (*x);
+               }
+
        }
        /* TrackSelection and RouteList leave scope,
         * destructors are called,
@@ -7658,7 +7708,7 @@ Editor::do_remove_time ()
 
 void
 Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
-                    bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
+                     bool ignore_music_glue, bool markers_too, bool glued_markers_too, bool locked_markers_too, bool tempo_too)
 {
        if (Config->get_edit_mode() == Lock) {
                error << (_("Cannot insert or delete time when in Lock edit.")) << endmsg;