Fix uninitialized variable
[ardour.git] / gtk2_ardour / editor_ops.cc
index 7173e267e65425d2c89c42df53689ccb7a5845a1..7a528929a8143453a4b759e6cee6f71d6c30ce3f 100644 (file)
 #include "ardour/legatize.h"
 #include "ardour/region_factory.h"
 #include "ardour/reverse.h"
+#include "ardour/selection.h"
 #include "ardour/session.h"
 #include "ardour/session_playlists.h"
 #include "ardour/strip_silence.h"
 #include "ardour/transient_detector.h"
+#include "ardour/transport_master_manager.h"
 #include "ardour/transpose.h"
 #include "ardour/vca_manager.h"
 
 #include "canvas/canvas.h"
 
 #include "actions.h"
+#include "ardour_ui.h"
 #include "audio_region_view.h"
 #include "audio_streamview.h"
 #include "audio_time_axis.h"
 #include "transpose_dialog.h"
 #include "transform_dialog.h"
 #include "ui_config.h"
+#include "utils.h"
 #include "vca_time_axis.h"
 
 #include "pbd/i18n.h"
@@ -173,7 +177,7 @@ Editor::redo (uint32_t n)
 }
 
 void
-Editor::split_regions_at (MusicSample where, RegionSelection& regions, bool snap_sample)
+Editor::split_regions_at (MusicSample where, RegionSelection& regions)
 {
        bool frozen = false;
 
@@ -189,25 +193,11 @@ Editor::split_regions_at (MusicSample where, RegionSelection& regions, bool snap
 
        begin_reversible_command (_("split"));
 
-       // if splitting a single region, and snap-to is using
-       // region boundaries, don't pay attention to them
 
        if (regions.size() == 1) {
-//             switch (_snap_type) {   //ToDo !!!
-//             case SnapToRegionStart:
-//             case SnapToRegionSync:
-//             case SnapToRegionEnd:
-//                     break;
-//             default:
-//                     if (snap_sample) {
-                               snap_to (where);
-//                     }
-//             }
+               /* TODO:  if splitting a single region, and snap-to is using
+                region boundaries, mabye we shouldn't pay attention to them? */
        } else {
-               if (snap_sample) {
-                       snap_to (where);
-               }
-
                frozen = true;
                EditorFreeze(); /* Emit Signal */
        }
@@ -708,12 +698,12 @@ void
 Editor::build_region_boundary_cache ()
 {
 
-       //ToDo:  maybe set a timer so we don't recalutate when lots of changes are coming in 
-       //ToDo:  maybe somehow defer this until session is fully loaded.
-       
-       if ( !_region_boundary_cache_dirty )
+       /* TODO:  maybe set a timer so we don't recalutate when lots of changes are coming in */
+       /* TODO:  maybe somehow defer this until session is fully loaded.  */
+
+       if (!_region_boundary_cache_dirty)
                return;
-       
+
        samplepos_t pos = 0;
        vector<RegionPoint> interesting_points;
        boost::shared_ptr<Region> r;
@@ -727,30 +717,30 @@ Editor::build_region_boundary_cache ()
        }
 
        bool maybe_first_sample = false;
-               
-       if ( UIConfiguration::instance().get_snap_to_region_start() ) {
+
+       if (UIConfiguration::instance().get_snap_to_region_start()) {
                interesting_points.push_back (Start);
                maybe_first_sample = true;
        }
-       
-       if ( UIConfiguration::instance().get_snap_to_region_end() ) {
+
+       if (UIConfiguration::instance().get_snap_to_region_end()) {
                interesting_points.push_back (End);
        }
-       
-       if ( UIConfiguration::instance().get_snap_to_region_sync() ) {
+
+       if (UIConfiguration::instance().get_snap_to_region_sync()) {
                interesting_points.push_back (SyncPoint);
        }
-       
+
+       /* if no snap selections are set, boundary cache should be left empty */
+       if ( interesting_points.empty() ) {
+               _region_boundary_cache_dirty = false;
+               return;
+       }
+
        TimeAxisView *ontrack = 0;
        TrackViewList tlist;
 
-       //in the past, we used the track selection to limit snap.  I think this is not desired.
-       //or if it is,  it needs to be updated every time the track selection changes (so the snapped-cursor can show it)
-//     if (!selection->tracks.empty()) {
-//             tlist = selection->tracks.filter_to_unique_playlists ();
-//     } else {
-               tlist = track_views.filter_to_unique_playlists ();
-//     }
+       tlist = track_views.filter_to_unique_playlists ();
 
        if (maybe_first_sample) {
                TrackViewList::const_iterator i;
@@ -763,10 +753,18 @@ Editor::build_region_boundary_cache ()
                }
        }
 
-       while (pos < _session->current_end_sample() && !at_end) {
+       //allow regions to snap to the video start (if any) as if it were a "region"
+       if (ARDOUR_UI::instance()->video_timeline) {
+               region_boundary_cache.push_back (ARDOUR_UI::instance()->video_timeline->get_video_start_offset());
+       }
+
+       std::pair<samplepos_t, samplepos_t> ext = session_gui_extents (false);
+       samplepos_t session_end = ext.second;
+
+       while (pos < session_end && !at_end) {
 
                samplepos_t rpos;
-               samplepos_t lpos = max_samplepos;
+               samplepos_t lpos = session_end;
 
                for (vector<RegionPoint>::iterator p = interesting_points.begin(); p != interesting_points.end(); ++p) {
 
@@ -822,7 +820,7 @@ Editor::build_region_boundary_cache ()
        /* finally sort to be sure that the order is correct */
 
        sort (region_boundary_cache.begin(), region_boundary_cache.end());
-       
+
        _region_boundary_cache_dirty = false;
 }
 
@@ -1962,13 +1960,13 @@ void
 Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
 {
        if (!selection) return;
-       
-       if ( selection->regions.empty() && selection->time.empty() ) {
+
+       if (selection->regions.empty() && selection->time.empty()) {
                if (axes == Horizontal || axes == Both) {
                        temporal_zoom_step(true);
                }
                if (axes == Vertical || axes == Both) {
-                       if ( !track_views.empty() ) {
+                       if (!track_views.empty()) {
 
                                TrackViewList tvl;
 
@@ -1977,7 +1975,7 @@ Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
                                const double btm = top + _visible_canvas_height + 10;
 
                                for (TrackViewList::iterator iter = track_views.begin(); iter != track_views.end(); ++iter) {
-                                       if ( (*iter)->covered_by_y_range (top, btm) ) {
+                                       if ((*iter)->covered_by_y_range (top, btm)) {
                                                tvl.push_back(*iter);
                                        }
                                }
@@ -2004,10 +2002,10 @@ Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
        if (axes == Vertical || axes == Both) {
                fit_selection ();
        }
-       
+
        //normally, we don't do anything "automatic" to the user's selection.
        //but in this case, we will clear the selection after a zoom-to-selection.
-       selection->clear();  
+       selection->clear();
 }
 
 void
@@ -2599,7 +2597,7 @@ Editor::transition_to_rolling (bool fwd)
        }
 
        if (_session->config.get_external_sync()) {
-               switch (Config->get_sync_source()) {
+               switch (TransportMasterManager::instance().current()->type()) {
                case Engine:
                        break;
                default:
@@ -4772,10 +4770,7 @@ Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num
                DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("preferred edit position is %1\n", position));
        }
 
-       if (position == last_paste_pos) {
-               /* repeated paste in the same position */
-               ++paste_count;
-       } else {
+       if (position != last_paste_pos) {
                /* paste in new location, reset repeated paste state */
                paste_count = 0;
                last_paste_pos = position;
@@ -4864,6 +4859,8 @@ Editor::paste_internal (samplepos_t position, float times, const int32_t sub_num
                }
        }
 
+       ++paste_count;
+
        commit_reversible_command ();
 }
 
@@ -4882,17 +4879,40 @@ Editor::duplicate_some_regions (RegionSelection& regions, float times)
        }
 
        boost::shared_ptr<Playlist> playlist;
+       std::set<boost::shared_ptr<Playlist> > playlists; // list of unique playlists affected by duplication
        RegionSelection sel = regions; // clear (below) may  clear the argument list if its the current region selection
        RegionSelection foo;
 
        samplepos_t const start_sample = regions.start ();
        samplepos_t const end_sample = regions.end_sample ();
-       samplecnt_t const gap = end_sample - start_sample + 1;
+       samplecnt_t const span = end_sample - start_sample + 1;
 
        begin_reversible_command (Operations::duplicate_region);
 
        selection->clear_regions ();
 
+       /* ripple first so that we don't move the duplicates that will be added */
+
+       if (Config->get_edit_mode() == Ripple) {
+
+               /* convert RegionSelection into RegionList so that we can pass it to ripple and exclude the regions we will duplicate */
+
+               RegionList exclude;
+
+               for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
+                       exclude.push_back ((*i)->region());
+                       playlist = (*i)->region()->playlist();
+                       if (playlists.insert (playlist).second) {
+                               /* successfully inserted into set, so it's the first time we've seen this playlist */
+                               playlist->clear_changes ();
+                       }
+               }
+
+               for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
+                       (*p)->ripple (start_sample, span * times, &exclude);
+               }
+       }
+
        for (RegionSelection::iterator i = sel.begin(); i != sel.end(); ++i) {
 
                boost::shared_ptr<Region> r ((*i)->region());
@@ -4904,15 +4924,27 @@ Editor::duplicate_some_regions (RegionSelection& regions, float times)
 
                samplepos_t const position = end_sample + (r->first_sample() - start_sample + 1);
                playlist = (*i)->region()->playlist();
-               playlist->clear_changes ();
-               playlist->duplicate (r, position, gap, times);
-               _session->add_command(new StatefulDiffCommand (playlist));
+
+               if (Config->get_edit_mode() != Ripple) {
+                       if (playlists.insert (playlist).second) {
+                               playlist->clear_changes ();
+                       }
+               }
+
+               playlist->duplicate (r, position, span, times);
 
                c.disconnect ();
 
                foo.insert (foo.end(), latest_regionviews.begin(), latest_regionviews.end());
        }
 
+       for (set<boost::shared_ptr<Playlist> >::iterator p = playlists.begin(); p != playlists.end(); ++p) {
+               _session->add_command (new StatefulDiffCommand (*p));
+               vector<Command*> cmds;
+               (*p)->rdiff (cmds);
+               _session->add_commands (cmds);
+       }
+
        if (!foo.empty()) {
                selection->set (foo);
        }
@@ -5891,18 +5923,18 @@ Editor::toggle_record_enable ()
 }
 
 StripableList
-tracklist_to_stripables( TrackViewList list )
+tracklist_to_stripables (TrackViewList list)
 {
        StripableList ret;
-       
+
        for (TrackSelection::iterator i = list.begin(); i != list.end(); ++i) {
                RouteTimeAxisView* rtv = dynamic_cast<RouteTimeAxisView*> ((*i));
 
                if (rtv && rtv->is_track()) {
-                       ret.push_back( rtv->track() );
+                       ret.push_back (rtv->track());
                }
        }
-       
+
        return ret;
 }
 
@@ -5911,25 +5943,25 @@ Editor::play_solo_selection (bool restart)
 {
        //note: session::solo_selection takes care of invalidating the region playlist
 
-       if ( (!selection->tracks.empty()) && selection->time.length() > 0 ) {  //a range is selected; solo the tracks and roll
-       
+       if ((!selection->tracks.empty()) && selection->time.length() > 0) {  //a range is selected; solo the tracks and roll
+
                StripableList sl = tracklist_to_stripables (selection->tracks);
-               _session->solo_selection( sl, true );
+               _session->solo_selection (sl, true);
 
-               if ( restart ) {
+               if (restart) {
                        samplepos_t start = selection->time.start();
                        samplepos_t end = selection->time.end_sample();
                        _session->request_bounded_roll (start, end);
                }
-       } else if ( ! selection->tracks.empty() ) {  //no range is selected, but tracks are selected; solo the tracks and roll
+       } else if (! selection->tracks.empty()) {  //no range is selected, but tracks are selected; solo the tracks and roll
                StripableList sl = tracklist_to_stripables (selection->tracks);
-               _session->solo_selection( sl, true );
+               _session->solo_selection (sl, true);
                _session->request_cancel_play_range();
                transition_to_rolling (true);
-       
-       } else if ( ! selection->regions.empty() ) {  //solo any tracks with selected regions, and roll
-               StripableList sl = tracklist_to_stripables ( get_tracks_for_range_action() );
-               _session->solo_selection( sl, true );
+
+       } else if (! selection->regions.empty()) {  //solo any tracks with selected regions, and roll
+               StripableList sl = tracklist_to_stripables (get_tracks_for_range_action());
+               _session->solo_selection (sl, true);
                _session->request_cancel_play_range();
                transition_to_rolling (true);
        } else {
@@ -6361,7 +6393,10 @@ Editor::set_playhead_cursor ()
 void
 Editor::split_region ()
 {
-       if (_drags->active ()) {
+       if (_dragging_playhead) {
+               /*continue*/
+       } else if (_drags->active ()) {
+               /*any other kind of drag, bail out so we avoid Undo snafu*/
                return;
        }
 
@@ -6372,7 +6407,7 @@ Editor::split_region ()
        }
 
        //if no range was selected, try to find some regions to split
-       if (current_mouse_mode() == MouseObject) {  //don't try this for Internal Edit, Stretch, Draw, etc.
+       if (current_mouse_mode() == MouseObject || current_mouse_mode() == MouseRange ) {  //don't try this for Internal Edit, Stretch, Draw, etc.
 
                RegionSelection rs = get_regions_from_selection_and_edit_point ();
                const samplepos_t pos = get_preferred_edit_position();
@@ -6391,79 +6426,13 @@ Editor::split_region ()
 void
 Editor::select_next_stripable (bool routes_only)
 {
-       if (selection->tracks.empty()) {
-               selection->set (track_views.front());
-               return;
-       }
-
-       TimeAxisView* current = selection->tracks.front();
-
-       bool valid;
-       do {
-               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-
-                       if (*i == current) {
-                               ++i;
-                               if (i != track_views.end()) {
-                                       current = (*i);
-                               } else {
-                                       current = (*(track_views.begin()));
-                                       //selection->set (*(track_views.begin()));
-                               }
-                               break;
-                       }
-               }
-
-               if (routes_only) {
-                       RouteUI* rui = dynamic_cast<RouteUI *>(current);
-                       valid = rui && rui->route()->active();
-               } else {
-                       valid = 0 != current->stripable ().get();
-               }
-
-       } while (current->hidden() || !valid);
-
-       selection->set (current);
-
-       ensure_time_axis_view_is_visible (*current, false);
+       _session->selection().select_next_stripable (false, routes_only);
 }
 
 void
 Editor::select_prev_stripable (bool routes_only)
 {
-       if (selection->tracks.empty()) {
-               selection->set (track_views.front());
-               return;
-       }
-
-       TimeAxisView* current = selection->tracks.front();
-
-       bool valid;
-       do {
-               for (TrackViewList::reverse_iterator i = track_views.rbegin(); i != track_views.rend(); ++i) {
-
-                       if (*i == current) {
-                               ++i;
-                               if (i != track_views.rend()) {
-                                       current = (*i);
-                               } else {
-                                       current = *(track_views.rbegin());
-                               }
-                               break;
-                       }
-               }
-               if (routes_only) {
-                       RouteUI* rui = dynamic_cast<RouteUI *>(current);
-                       valid = rui && rui->route()->active();
-               } else {
-                       valid = 0 != current->stripable ().get();
-               }
-
-       } while (current->hidden() || !valid);
-
-       selection->set (current);
-
-       ensure_time_axis_view_is_visible (*current, false);
+       _session->selection().select_prev_stripable (false, routes_only);
 }
 
 void
@@ -6617,11 +6586,8 @@ Editor::set_punch_start_from_edit_point ()
                        start.sample = get_preferred_edit_position();
                }
 
-               //snap the selection start/end
-               snap_to(start);
-
                //if there's not already a sensible selection endpoint, go "forever"
-               if (start.sample > end ) {
+               if (start.sample > end) {
                        end = max_samplepos;
                }
 
@@ -6650,9 +6616,6 @@ Editor::set_punch_end_from_edit_point ()
                        end.sample = get_preferred_edit_position();
                }
 
-               //snap the selection start/end
-               snap_to (end);
-
                set_punch_range (start, end.sample, _("set punch end from EP"));
 
        }
@@ -6678,11 +6641,8 @@ Editor::set_loop_start_from_edit_point ()
                        start.sample = get_preferred_edit_position();
                }
 
-               //snap the selection start/end
-               snap_to (start);
-
                //if there's not already a sensible selection endpoint, go "forever"
-               if (start.sample > end ) {
+               if (start.sample > end) {
                        end = max_samplepos;
                }
 
@@ -6711,9 +6671,6 @@ Editor::set_loop_end_from_edit_point ()
                        end.sample = get_preferred_edit_position();
                }
 
-               //snap the selection start/end
-               snap_to(end);
-
                set_loop_range (start, end.sample, _("set loop end from EP"));
        }
 }
@@ -7132,7 +7089,7 @@ Editor::snap_regions_to_grid ()
                (*r)->region()->clear_changes ();
 
                MusicSample start ((*r)->region()->first_sample (), 0);
-               snap_to (start, RoundNearest, SnapToGrid );
+               snap_to (start, RoundNearest, SnapToGrid_Unscaled, true);
                (*r)->region()->set_position (start.sample, start.division);
                _session->add_command(new StatefulDiffCommand ((*r)->region()));
        }
@@ -7337,12 +7294,28 @@ Editor::playhead_forward_to_grid ()
                return;
        }
 
-       MusicSample pos (playhead_cursor->current_sample (), 0);
+       MusicSample pos  (playhead_cursor->current_sample (), 0);
+
+       if ( _grid_type == GridTypeNone) {
+               if (pos.sample < max_samplepos - current_page_samples()*0.1) {
+                       pos.sample += current_page_samples()*0.1;
+                       _session->request_locate (pos.sample);
+               } else {
+                       _session->request_locate (0);
+               }
+       } else {
 
-       if (pos.sample < max_samplepos - 1) {
-               pos.sample += 2;
-               snap_to_internal (pos, RoundUpAlways, SnapToGrid, false, true);
-               _session->request_locate (pos.sample);
+               if (pos.sample < max_samplepos - 1) {
+                       pos.sample += 2;
+                       pos = snap_to_grid (pos, RoundUpAlways, SnapToGrid_Scaled);
+                       _session->request_locate (pos.sample);
+               }
+       }
+
+
+       /* keep PH visible in window */
+       if (pos.sample > (_leftmost_sample + current_page_samples() *0.9)) {
+               reset_x_origin (pos.sample - (current_page_samples()*0.9));
        }
 }
 
@@ -7356,10 +7329,34 @@ Editor::playhead_backward_to_grid ()
 
        MusicSample pos  (playhead_cursor->current_sample (), 0);
 
-       if (pos.sample > 2) {
-               pos.sample -= 2;
-               snap_to_internal (pos, RoundDownAlways, SnapToGrid, false, true);
-               _session->request_locate (pos.sample);
+       if ( _grid_type == GridTypeNone) {
+               if ( pos.sample > current_page_samples()*0.1 ) {
+                       pos.sample -= current_page_samples()*0.1;
+                       _session->request_locate (pos.sample);
+               } else {
+                       _session->request_locate (0);
+               }
+       } else {
+
+               if (pos.sample > 2) {
+                       pos.sample -= 2;
+                       pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
+               }
+
+               //handle the case where we are rolling, and we're less than one-half second past the mark, we want to go to the prior mark...
+               //also see:  jump_backward_to_mark
+               if (_session->transport_rolling()) {
+                       if ((playhead_cursor->current_sample() - pos.sample) < _session->sample_rate()/2) {
+                               pos = snap_to_grid (pos, RoundDownAlways, SnapToGrid_Scaled);
+                       }
+               }
+
+               _session->request_locate (pos.sample, _session->transport_rolling());
+       }
+
+       /* keep PH visible in window */
+       if (pos.sample < (_leftmost_sample + current_page_samples() *0.1)) {
+               reset_x_origin (pos.sample - (current_page_samples()*0.1));
        }
 }
 
@@ -7424,6 +7421,10 @@ Editor::_remove_tracks ()
                return;
        }
 
+       if (!ARDOUR_UI_UTILS::engine_is_running ()) {
+               return;
+       }
+
        vector<string> choices;
        string prompt;
        int ntracks = 0;
@@ -7549,7 +7550,7 @@ edit your ardour.rc file to set the\n\
                /* Route deletion calls Editor::timeaxisview_deleted() iteratively (for each deleted
                 * route). If the deleted route is currently displayed in the Editor-Mixer (highly
                 * likely because deletion requires selection) this will call
-                * Editor::set_selected_mixer_strip () which is expensive ( MixerStrip::set_route() ).
+                * Editor::set_selected_mixer_strip () which is expensive (MixerStrip::set_route()).
                 * It's likewise likely that the route that has just been displayed in the
                 * Editor-Mixer will be next in line for deletion.
                 *
@@ -7588,6 +7589,18 @@ void
 Editor::do_insert_time ()
 {
        if (selection->tracks.empty()) {
+               MessageDialog msg (_("You must first select some tracks to Insert Time."),
+                                  true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.set_position (WIN_POS_MOUSE);
+               msg.run ();
+               return;
+       }
+
+       if (Config->get_edit_mode() == Lock) {
+               MessageDialog msg (_("You cannot insert time in Lock Edit mode."),
+                                  true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.set_position (WIN_POS_MOUSE);
+               msg.run ();
                return;
        }
 
@@ -7751,6 +7764,18 @@ void
 Editor::do_remove_time ()
 {
        if (selection->tracks.empty()) {
+               MessageDialog msg (_("You must first select some tracks to Remove Time."),
+                                  true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.set_position (WIN_POS_MOUSE);
+               msg.run ();
+               return;
+       }
+
+       if (Config->get_edit_mode() == Lock) {
+               MessageDialog msg (_("You cannot remove time in Lock Edit mode."),
+                                  true, MESSAGE_INFO, BUTTONS_OK, true);
+               msg.set_position (WIN_POS_MOUSE);
+               msg.run ();
                return;
        }