Fix crash when showing plugin-UI in safe-mode
[ardour.git] / gtk2_ardour / editor_ops.cc
index e5d762332e9fd72828d5a9f099d365042a55427f..ecc9fa1f06c9c7382a9ee7e02ee3ee355cdaabcd 100644 (file)
@@ -82,6 +82,7 @@
 #include "item_counts.h"
 #include "keyboard.h"
 #include "midi_region_view.h"
+#include "mixer_ui.h"
 #include "mixer_strip.h"
 #include "mouse_cursors.h"
 #include "normalize_dialog.h"
@@ -119,6 +120,13 @@ using Gtkmm2ext::Keyboard;
 void
 Editor::undo (uint32_t n)
 {
+       if (_session && _session->actively_recording()) {
+               /* no undo allowed while recording. Session will check also,
+                  but we don't even want to get to that.
+               */
+               return;
+       }
+
        if (_drags->active ()) {
                _drags->abort ();
        }
@@ -136,12 +144,19 @@ Editor::undo (uint32_t n)
 void
 Editor::redo (uint32_t n)
 {
+       if (_session && _session->actively_recording()) {
+               /* no redo allowed while recording. Session will check also,
+                  but we don't even want to get to that.
+               */
+               return;
+       }
+
        if (_drags->active ()) {
                _drags->abort ();
        }
 
        if (_session) {
-               _session->redo (n);
+       _session->redo (n);
                if (_session->redo_depth() == 0) {
                        redo_action->set_sensitive(false);
                }
@@ -151,7 +166,7 @@ Editor::redo (uint32_t n)
 }
 
 void
-Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int32_t sub_num)
+Editor::split_regions_at (MusicFrame where, RegionSelection& regions, bool snap_frame)
 {
        bool frozen = false;
 
@@ -177,10 +192,14 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
                case SnapToRegionEnd:
                        break;
                default:
-                       snap_to (where);
+                       if (snap_frame) {
+                               snap_to (where);
+                       }
                }
        } else {
-               snap_to (where);
+               if (snap_frame) {
+                       snap_to (where);
+               }
 
                frozen = true;
                EditorFreeze(); /* Emit Signal */
@@ -194,7 +213,7 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
                   have something to split.
                */
 
-               if (!(*a)->region()->covers (where)) {
+               if (!(*a)->region()->covers (where.frame)) {
                        ++a;
                        continue;
                }
@@ -226,7 +245,7 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
 
                if (pl) {
                        pl->clear_changes ();
-                       pl->split_region ((*a)->region(), where, sub_num);
+                       pl->split_region ((*a)->region(), where);
                        _session->add_command (new StatefulDiffCommand (pl));
                }
 
@@ -258,7 +277,6 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
        if (working_on_selection) {
                // IFF we were working on selected regions, try to reinstate the other region selections that existed before the freeze/thaw.
 
-               _ignore_follow_edits = true;  // a split will change the region selection in mysterious ways;  it's not practical or wanted to follow this edit
                RegionSelectionAfterSplit rsas = Config->get_region_selection_after_split();
                /* There are three classes of regions that we might want selected after
                   splitting selected regions:
@@ -273,7 +291,7 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
                }
 
                for (RegionSelection::iterator ri = latest_regionviews.begin(); ri != latest_regionviews.end(); ri++) {
-                       if ((*ri)->region()->position() < where) {
+                       if ((*ri)->region()->position() < where.frame) {
                                // new regions created before the split
                                if (rsas & NewlyCreatedLeft) {
                                        selection->add (*ri);
@@ -285,13 +303,10 @@ Editor::split_regions_at (framepos_t where, RegionSelection& regions, const int3
                                }
                        }
                }
-               _ignore_follow_edits = false;
        } else {
-               _ignore_follow_edits = true;
                if( working_on_selection ) {
                        selection->add (latest_regionviews);  //these are the new regions created after the split
                }
-               _ignore_follow_edits = false;
        }
 
        commit_reversible_command ();
@@ -404,6 +419,7 @@ Editor::nudge_forward (bool next, bool force_playhead)
 
                bool is_start;
                bool in_command = false;
+               const int32_t divisions = get_grid_music_divisions (0);
 
                for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
 
@@ -419,9 +435,9 @@ Editor::nudge_forward (bool next, bool force_playhead)
                                                distance = next_distance;
                                        }
                                        if (max_framepos - distance > loc->start() + loc->length()) {
-                                               loc->set_start (loc->start() + distance);
+                                               loc->set_start (loc->start() + distance, false, true, divisions);
                                        } else {
-                                               loc->set_start (max_framepos - loc->length());
+                                               loc->set_start (max_framepos - loc->length(), false, true, divisions);
                                        }
                                } else {
                                        distance = get_nudge_distance (loc->end(), next_distance);
@@ -429,9 +445,9 @@ Editor::nudge_forward (bool next, bool force_playhead)
                                                distance = next_distance;
                                        }
                                        if (max_framepos - distance > loc->end()) {
-                                               loc->set_end (loc->end() + distance);
+                                               loc->set_end (loc->end() + distance, false, true, divisions);
                                        } else {
-                                               loc->set_end (max_framepos);
+                                               loc->set_end (max_framepos, false, true, divisions);
                                        }
                                        if (loc->is_session_range()) {
                                                _session->set_end_is_free (false);
@@ -511,9 +527,9 @@ Editor::nudge_backward (bool next, bool force_playhead)
                                                distance = next_distance;
                                        }
                                        if (distance < loc->start()) {
-                                               loc->set_start (loc->start() - distance);
+                                               loc->set_start (loc->start() - distance, false, true, get_grid_music_divisions(0));
                                        } else {
-                                               loc->set_start (0);
+                                               loc->set_start (0, false, true, get_grid_music_divisions(0));
                                        }
                                } else {
                                        distance = get_nudge_distance (loc->end(), next_distance);
@@ -523,9 +539,9 @@ Editor::nudge_backward (bool next, bool force_playhead)
                                        }
 
                                        if (distance < loc->end() - loc->length()) {
-                                               loc->set_end (loc->end() - distance);
+                                               loc->set_end (loc->end() - distance, false, true, get_grid_music_divisions(0));
                                        } else {
-                                               loc->set_end (loc->length());
+                                               loc->set_end (loc->length(), false, true, get_grid_music_divisions(0));
                                        }
                                        if (loc->is_session_range()) {
                                                _session->set_end_is_free (false);
@@ -1139,7 +1155,7 @@ Editor::selected_marker_to_region_boundary (bool with_selection, int32_t dir)
                return;
        }
 
-       loc->move_to (target);
+       loc->move_to (target, 0);
 }
 
 void
@@ -1216,7 +1232,7 @@ Editor::selected_marker_to_region_point (RegionPoint point, int32_t dir)
 
        pos = track_frame_to_session_frame(pos, speed);
 
-       loc->move_to (pos);
+       loc->move_to (pos, 0);
 }
 
 void
@@ -1263,7 +1279,7 @@ Editor::selected_marker_to_selection_start ()
                return;
        }
 
-       loc->move_to (pos);
+       loc->move_to (pos, 0);
 }
 
 void
@@ -1298,7 +1314,7 @@ Editor::selected_marker_to_selection_end ()
                return;
        }
 
-       loc->move_to (pos);
+       loc->move_to (pos, 0);
 }
 
 void
@@ -1350,6 +1366,7 @@ Editor::cursor_align (bool playhead_to_edit)
                _session->request_locate (selection->markers.front()->position(), _session->transport_rolling());
 
        } else {
+               const int32_t divisions = get_grid_music_divisions (0);
                /* move selected markers to playhead */
 
                for (MarkerSelection::iterator i = selection->markers.begin(); i != selection->markers.end(); ++i) {
@@ -1358,10 +1375,10 @@ Editor::cursor_align (bool playhead_to_edit)
                        Location* loc = find_location_from_marker (*i, ignored);
 
                        if (loc->is_mark()) {
-                               loc->set_start (playhead_cursor->current_frame ());
+                               loc->set_start (playhead_cursor->current_frame (), false, true, divisions);
                        } else {
                                loc->set (playhead_cursor->current_frame (),
-                                         playhead_cursor->current_frame () + loc->length());
+                                         playhead_cursor->current_frame () + loc->length(), true, divisions);
                        }
                }
        }
@@ -1434,6 +1451,22 @@ Editor::scroll_tracks_up_line ()
        reset_y_origin (vertical_adjustment.get_value() - 60);
 }
 
+void
+Editor::select_topmost_track ()
+{
+       const double top_of_trackviews = vertical_adjustment.get_value();
+       for (TrackViewList::iterator t = track_views.begin(); t != track_views.end(); ++t) {
+               if ((*t)->hidden()) {
+                       continue;
+               }
+               std::pair<TimeAxisView*,double> res = (*t)->covers_y_position (top_of_trackviews);
+               if (res.first) {
+                       selection->set (*t);
+                       break;
+               }
+       }
+}
+
 bool
 Editor::scroll_down_one_track (bool skip_child_views)
 {
@@ -1695,25 +1728,43 @@ Editor::tav_zoom_smooth (bool coarser, bool force_all)
 }
 
 void
-Editor::temporal_zoom_step_mouse_focus (bool coarser)
+Editor::temporal_zoom_step_mouse_focus_scale (bool zoom_out, double scale)
 {
        Editing::ZoomFocus temp_focus = zoom_focus;
        zoom_focus = Editing::ZoomFocusMouse;
-       temporal_zoom_step (coarser);
+       temporal_zoom_step_scale (zoom_out, scale);
        zoom_focus = temp_focus;
 }
 
 void
-Editor::temporal_zoom_step (bool coarser)
+Editor::temporal_zoom_step_mouse_focus (bool zoom_out)
 {
-       ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, coarser)
+       temporal_zoom_step_mouse_focus_scale (zoom_out, 2.0);
+}
+
+void
+Editor::temporal_zoom_step (bool zoom_out)
+{
+       temporal_zoom_step_scale (zoom_out, 2.0);
+}
+
+void
+Editor::temporal_zoom_step_scale (bool zoom_out, double scale)
+{
+       ENSURE_GUI_THREAD (*this, &Editor::temporal_zoom_step, zoom_out, scale)
 
        framecnt_t nspp = samples_per_pixel;
 
-       if (coarser) {
-               nspp *= 2;
+       if (zoom_out) {
+               nspp *= scale;
+               if (nspp == samples_per_pixel) {
+                       nspp *= 2.0;
+               }
        } else {
-               nspp /= 2;
+               nspp /= scale;
+               if (nspp == samples_per_pixel) {
+                       nspp /= 2.0;
+               }
        }
 
        temporal_zoom (nspp);
@@ -1735,6 +1786,7 @@ Editor::temporal_zoom (framecnt_t fpp)
        framepos_t leftmost_after_zoom = 0;
        framepos_t where;
        bool in_track_canvas;
+       bool use_mouse_frame = true;
        framecnt_t nfpp;
        double l;
 
@@ -1795,18 +1847,13 @@ Editor::temporal_zoom (framecnt_t fpp)
        case ZoomFocusMouse:
                /* try to keep the mouse over the same point in the display */
 
-               if (!mouse_frame (where, in_track_canvas)) {
-                       /* use playhead instead */
-                       where = playhead_cursor->current_frame ();
-
-                       if (where < half_page_size) {
-                               leftmost_after_zoom = 0;
-                       } else {
-                               leftmost_after_zoom = where - half_page_size;
-                       }
-
-               } else {
+               if (_drags->active()) {
+                       where = _drags->current_pointer_frame ();
+               } else if (!mouse_frame (where, in_track_canvas)) {
+                       use_mouse_frame = false;
+               }
 
+               if (use_mouse_frame) {
                        l = - ((new_page_size * ((where - current_leftmost)/(double)current_page)) - where);
 
                        if (l < 0) {
@@ -1816,8 +1863,16 @@ Editor::temporal_zoom (framecnt_t fpp)
                        } else {
                                leftmost_after_zoom = (framepos_t) l;
                        }
-               }
+               } else {
+                       /* use playhead instead */
+                       where = playhead_cursor->current_frame ();
 
+                       if (where < half_page_size) {
+                               leftmost_after_zoom = 0;
+                       } else {
+                               leftmost_after_zoom = where - half_page_size;
+                       }
+               }
                break;
 
        case ZoomFocusEdit:
@@ -1880,53 +1935,6 @@ Editor::calc_extra_zoom_edges(framepos_t &start, framepos_t &end)
        }
 }
 
-void
-Editor::temporal_zoom_region (bool both_axes)
-{
-       framepos_t start = max_framepos;
-       framepos_t end = 0;
-       set<TimeAxisView*> tracks;
-
-       if ( !get_selection_extents(start, end) )
-               return;
-
-       calc_extra_zoom_edges (start, end);
-
-       /* if we're zooming on both axes we need to save track heights etc.
-        */
-
-       undo_visual_stack.push_back (current_visual_state (both_axes));
-
-       PBD::Unwinder<bool> nsv (no_save_visual, true);
-
-       temporal_zoom_by_frame (start, end);
-
-       if (both_axes) {
-               uint32_t per_track_height = (uint32_t) floor ((_visible_canvas_height - 10.0) / tracks.size());
-
-               /* set visible track heights appropriately */
-
-               for (set<TimeAxisView*>::iterator t = tracks.begin(); t != tracks.end(); ++t) {
-                       (*t)->set_height (per_track_height);
-               }
-
-               /* hide irrelevant tracks */
-
-               DisplaySuspender ds;
-
-               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-                       if (find (tracks.begin(), tracks.end(), (*i)) == tracks.end()) {
-                               hide_track_in_display (*i);
-                       }
-               }
-
-               vertical_adjustment.set_value (0.0);
-       }
-
-       redo_visual_stack.push_back (current_visual_state (both_axes));
-}
-
-
 bool
 Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
 {
@@ -1968,7 +1976,7 @@ Editor::get_selection_extents (framepos_t &start, framepos_t &end) const
 
 
 void
-Editor::temporal_zoom_selection (bool both_axes)
+Editor::temporal_zoom_selection (Editing::ZoomAxis axes)
 {
        if (!selection) return;
 
@@ -1976,23 +1984,18 @@ Editor::temporal_zoom_selection (bool both_axes)
 
        //ToDo:  if control points are selected, zoom to that
 
-       //if region(s) are selected, zoom to that
-       if ( !selection->regions.empty() )
-               temporal_zoom_region (both_axes);
-
-       //if a range is selected, zoom to that
-       if (!selection->time.empty()) {
+       if (axes == Horizontal || axes == Both) {
 
-               framepos_t start,  end;
+               framepos_t start, end;
                if (get_selection_extents (start, end)) {
-                       calc_extra_zoom_edges(start, end);
+                       calc_extra_zoom_edges (start, end);
                        temporal_zoom_by_frame (start, end);
                }
-
-               if (both_axes)
-                       fit_selection();
        }
 
+       if (axes == Vertical || axes == Both) {
+               fit_selection ();
+       }
 }
 
 void
@@ -2159,7 +2162,7 @@ Editor::add_location_from_selection ()
        framepos_t end = selection->time[clicked_selection].end;
 
        _session->locations()->next_available_name(rangename,"selection");
-       Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker);
+       Location *location = new Location (*_session, start, end, rangename, Location::IsRangeMarker, get_grid_music_divisions(0));
 
        begin_reversible_command (_("add marker"));
 
@@ -2182,7 +2185,7 @@ Editor::add_location_mark (framepos_t where)
        if (!choose_new_marker_name(markername)) {
                return;
        }
-       Location *location = new Location (*_session, where, where, markername, Location::IsMark);
+       Location *location = new Location (*_session, where, where, markername, Location::IsMark, get_grid_music_divisions (0));
        begin_reversible_command (_("add marker"));
 
        XMLNode &before = _session->locations()->get_state();
@@ -2311,7 +2314,7 @@ Editor::add_locations_from_region ()
 
                boost::shared_ptr<Region> region = (*i)->region ();
 
-               Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker);
+               Location *location = new Location (*_session, region->position(), region->last_frame(), region->name(), Location::IsRangeMarker, 0);
 
                _session->locations()->add (location, true);
                commit = true;
@@ -2352,7 +2355,7 @@ Editor::add_location_from_region ()
        }
 
        // single range spanning all selected
-       Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker);
+       Location *location = new Location (*_session, selection->regions.start(), selection->regions.end_frame(), markername, Location::IsRangeMarker, 0);
        _session->locations()->add (location, true);
 
        begin_reversible_command (_("add marker"));
@@ -2388,6 +2391,14 @@ Editor::jump_backward_to_mark ()
 
        framepos_t pos = _session->locations()->first_mark_before (playhead_cursor->current_frame());
 
+       //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...
+       if ( _session->transport_rolling() ) {
+               if ( (playhead_cursor->current_frame() - pos) < _session->frame_rate()/2 ) {
+                       framepos_t prior = _session->locations()->first_mark_before ( pos );
+                       pos = prior;
+               }
+       }
+
        if (pos < 0) {
                return;
        }
@@ -2407,7 +2418,7 @@ Editor::set_mark ()
                return;
        }
 
-       _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark), true);
+       _session->locations()->add (new Location (*_session, pos, 0, markername, Location::IsMark, 0), true);
 }
 
 void
@@ -2610,24 +2621,19 @@ Editor::play_selection ()
        _session->request_play_range (&lar, true);
 }
 
-framepos_t
-Editor::get_preroll ()
-{
-       return Config->get_preroll_seconds() * _session->frame_rate();
-}
-
 
 void
-Editor::maybe_locate_with_edit_preroll ( framepos_t location )
+Editor::maybe_locate_with_edit_preroll (framepos_t location)
 {
-       if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _ignore_follow_edits || _session->config.get_external_sync() )
+       if ( _session->transport_rolling() || !UIConfiguration::instance().get_follow_edits() || _session->config.get_external_sync() )
                return;
 
-       location -= get_preroll();
+       location -= _session->preroll_samples (location);
 
        //don't try to locate before the beginning of time
-       if ( location < 0 )
+       if (location < 0) {
                location = 0;
+       }
 
        //if follow_playhead is on, keep the playhead on the screen
        if ( _follow_playhead )
@@ -2640,15 +2646,15 @@ Editor::maybe_locate_with_edit_preroll ( framepos_t location )
 void
 Editor::play_with_preroll ()
 {
-       {
-               framepos_t preroll = get_preroll();
+       framepos_t start, end;
+       if ( UIConfiguration::instance().get_follow_edits() && get_selection_extents ( start, end) ) {
+               const framepos_t preroll = _session->preroll_samples (start);
 
-               framepos_t start, end;
-               if (!get_selection_extents ( start, end))
-                       return;
+               framepos_t ret = start;
 
-               if (start > preroll)
+               if (start > preroll) {
                        start = start - preroll;
+               }
 
                end = end + preroll;  //"post-roll"
 
@@ -2657,9 +2663,35 @@ Editor::play_with_preroll ()
                lar.push_back (ar);
 
                _session->request_play_range (&lar, true);
+               _session->set_requested_return_frame (ret);  //force auto-return to return to range start, without the preroll
+       } else {
+               framepos_t ph = playhead_cursor->current_frame ();
+               const framepos_t preroll = _session->preroll_samples (ph);
+               framepos_t start;
+               if (ph > preroll) {
+                       start = ph - preroll;
+               } else {
+                       start = 0;
+               }
+               _session->request_locate (start, true);
+               _session->set_requested_return_frame (ph);  //force auto-return to return to playhead location, without the preroll
        }
 }
 
+void
+Editor::rec_with_preroll ()
+{
+       framepos_t ph = playhead_cursor->current_frame ();
+       framepos_t preroll = _session->preroll_samples (ph);
+       _session->request_preroll_record_trim (ph, preroll);
+}
+
+void
+Editor::rec_with_count_in ()
+{
+       _session->request_count_in_record ();
+}
+
 void
 Editor::play_location (Location& location)
 {
@@ -3693,10 +3725,8 @@ Editor::trim_region (bool front)
 
                        if (front) {
                                (*i)->region()->trim_front (where);
-                               maybe_locate_with_edit_preroll ( where );
                        } else {
                                (*i)->region()->trim_end (where);
-                               maybe_locate_with_edit_preroll ( where );
                        }
 
                        _session->add_command (new StatefulDiffCommand ((*i)->region()));
@@ -4228,7 +4258,7 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
 
        /* user could select points in any order */
        selection->points.sort(PointsSelectionPositionSorter ());
-       
+
        /* Go through all selected points, making an AutomationRecord for each distinct AutomationList */
        for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
                const AutomationLine&                   line = (*sel_point)->line();
@@ -4250,7 +4280,7 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
                }
 
                /* Add all selected points to the relevant copy ControlLists */
-               framepos_t start = std::numeric_limits<framepos_t>::max();
+               MusicFrame start (std::numeric_limits<framepos_t>::max(), 0);
                for (PointSelection::iterator sel_point = selection->points.begin(); sel_point != selection->points.end(); ++sel_point) {
                        boost::shared_ptr<AutomationList>    al = (*sel_point)->line().the_list();
                        AutomationList::const_iterator ctrl_evt = (*sel_point)->model ();
@@ -4261,7 +4291,7 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
                                earliest = std::min(earliest, Evoral::Beats((*ctrl_evt)->when));
                        } else {
                                /* Update earliest session start time in frames */
-                               start = std::min(start, (*sel_point)->line().session_position(ctrl_evt));
+                               start.frame = std::min(start.frame, (*sel_point)->line().session_position(ctrl_evt));
                        }
                }
 
@@ -4272,13 +4302,13 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
                        }
                        earliest.round_down_to_beat();
                } else {
-                       if (start == std::numeric_limits<double>::max()) {
-                               start = 0;  // Weird... don't offset
+                       if (start.frame == std::numeric_limits<double>::max()) {
+                               start.frame = 0;  // Weird... don't offset
                        }
                        snap_to(start, RoundDownMaybe);
                }
 
-               const double line_offset = midi ? earliest.to_double() : start;
+               const double line_offset = midi ? earliest.to_double() : start.frame;
                for (Lists::iterator i = lists.begin(); i != lists.end(); ++i) {
                        /* Correct this copy list so that it is relative to the earliest
                           start time, so relative ordering between points is preserved
@@ -4307,7 +4337,7 @@ Editor::cut_copy_points (Editing::CutCopyOp op, Evoral::Beats earliest, bool mid
                        boost::shared_ptr<AutomationList> al = line.the_list();
 
                        bool erase = true;
-                       
+
                        if (dynamic_cast<AudioRegionGainLine*> (&line)) {
                                /* removing of first and last gain point in region gain lines is prohibited*/
                                if (line.is_last_point (*(*sel_point)) || line.is_first_point (*(*sel_point))) {
@@ -4681,22 +4711,21 @@ void
 Editor::paste (float times, bool from_context)
 {
         DEBUG_TRACE (DEBUG::CutNPaste, "paste to preferred edit pos\n");
-
-       paste_internal (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), times, get_grid_music_divisions (0));
+       MusicFrame where (get_preferred_edit_position (EDIT_IGNORE_NONE, from_context), 0);
+       paste_internal (where.frame, times, 0);
 }
 
 void
 Editor::mouse_paste ()
 {
-       framepos_t where;
+       MusicFrame where (0, 0);
        bool ignored;
-
-       if (!mouse_frame (where, ignored)) {
+       if (!mouse_frame (where.frame, ignored)) {
                return;
        }
 
        snap_to (where);
-       paste_internal (where, 1, get_grid_music_divisions (0));
+       paste_internal (where.frame, 1, where.division);
 }
 
 void
@@ -4808,6 +4837,13 @@ Editor::paste_internal (framepos_t position, float times, const int32_t sub_num)
        commit_reversible_command ();
 }
 
+void
+Editor::duplicate_regions (float times)
+{
+       RegionSelection rs (get_regions_from_selection_and_entered());
+       duplicate_some_regions (rs, times);
+}
+
 void
 Editor::duplicate_some_regions (RegionSelection& regions, float times)
 {
@@ -4888,18 +4924,19 @@ Editor::duplicate_selection (float times)
        }
 
        if (in_command) {
-               // now "move" range selection to after the current range selection
-               framecnt_t distance = 0;
+               if (times == 1.0f) {
+                       // now "move" range selection to after the current range selection
+                       framecnt_t distance = 0;
 
-               if (clicked_selection) {
-                       distance = selection->time[clicked_selection].end -
-                                  selection->time[clicked_selection].start;
-               } else {
-                       distance = selection->time.end_frame() - selection->time.start();
-               }
-
-               selection->move_time (distance);
+                       if (clicked_selection) {
+                               distance =
+                                   selection->time[clicked_selection].end - selection->time[clicked_selection].start;
+                       } else {
+                               distance = selection->time.end_frame () - selection->time.start ();
+                       }
 
+                       selection->move_time (distance);
+               }
                commit_reversible_command ();
        }
 }
@@ -5035,7 +5072,7 @@ Editor::normalize_region ()
 
        NormalizeDialog dialog (rs.size() > 1);
 
-       if (dialog.run () == RESPONSE_CANCEL) {
+       if (dialog.run () != RESPONSE_ACCEPT) {
                return;
        }
 
@@ -5196,6 +5233,38 @@ Editor::adjust_region_gain (bool up)
        }
 }
 
+void
+Editor::reset_region_gain ()
+{
+       RegionSelection rs = get_regions_from_selection_and_entered ();
+
+       if (!_session || rs.empty()) {
+               return;
+       }
+
+       bool in_command = false;
+
+       for (RegionSelection::iterator r = rs.begin(); r != rs.end(); ++r) {
+               AudioRegionView* const arv = dynamic_cast<AudioRegionView*>(*r);
+               if (!arv) {
+                       continue;
+               }
+
+               arv->region()->clear_changes ();
+
+               arv->audio_region()->set_scale_amplitude (1.0f);
+
+               if (!in_command) {
+                               begin_reversible_command ("reset region gain");
+                               in_command = true;
+               }
+               _session->add_command (new StatefulDiffCommand (arv->region()));
+       }
+
+       if (in_command) {
+               commit_reversible_command ();
+       }
+}
 
 void
 Editor::reverse_region ()
@@ -5360,6 +5429,11 @@ Editor::quantize_regions (const RegionSelection& rs)
                quantize_dialog = new QuantizeDialog (*this);
        }
 
+       if (quantize_dialog->is_mapped()) {
+               /* in progress already */
+               return;
+       }
+
        quantize_dialog->present ();
        const int r = quantize_dialog->run ();
        quantize_dialog->hide ();
@@ -5722,11 +5796,17 @@ Editor::toggle_region_lock_style ()
                return;
        }
 
-       begin_reversible_command (_("region lock style"));
+       Glib::RefPtr<ToggleAction> a = Glib::RefPtr<ToggleAction>::cast_dynamic (_region_actions->get_action("toggle-region-lock-style"));
+       vector<Widget*> proxies = a->get_proxies();
+       Gtk::CheckMenuItem* cmi = dynamic_cast<Gtk::CheckMenuItem*> (proxies.front());
+
+       assert (cmi);
+
+       begin_reversible_command (_("toggle region lock style"));
 
        for (RegionSelection::iterator i = rs.begin(); i != rs.end(); ++i) {
                (*i)->region()->clear_changes ();
-               PositionLockStyle const ns = (*i)->region()->position_lock_style() == AudioTime ? MusicTime : AudioTime;
+               PositionLockStyle const ns = ((*i)->region()->position_lock_style() == AudioTime && !cmi->get_inconsistent()) ? MusicTime : AudioTime;
                (*i)->region()->set_position_lock_style (ns);
                _session->add_command (new StatefulDiffCommand ((*i)->region()));
        }
@@ -6148,10 +6228,10 @@ Editor::update_region_fade_visibility ()
 void
 Editor::set_edit_point ()
 {
-       framepos_t where;
        bool ignored;
+       MusicFrame where (0, 0);
 
-       if (!mouse_frame (where, ignored)) {
+       if (!mouse_frame (where.frame, ignored)) {
                return;
        }
 
@@ -6159,7 +6239,7 @@ Editor::set_edit_point ()
 
        if (selection->markers.empty()) {
 
-               mouse_add_new_marker (where);
+               mouse_add_new_marker (where.frame);
 
        } else {
                bool ignored;
@@ -6167,7 +6247,7 @@ Editor::set_edit_point ()
                Location* loc = find_location_from_marker (selection->markers.front(), ignored);
 
                if (loc) {
-                       loc->move_to (where);
+                       loc->move_to (where.frame, where.division);
                }
        }
 }
@@ -6178,23 +6258,25 @@ Editor::set_playhead_cursor ()
        if (entered_marker) {
                _session->request_locate (entered_marker->position(), _session->transport_rolling());
        } else {
-               framepos_t where;
+               MusicFrame where (0, 0);
                bool ignored;
 
-               if (!mouse_frame (where, ignored)) {
+               if (!mouse_frame (where.frame, ignored)) {
                        return;
                }
 
                snap_to (where);
 
                if (_session) {
-                       _session->request_locate (where, _session->transport_rolling());
+                       _session->request_locate (where.frame, _session->transport_rolling());
                }
        }
 
-       if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
-               cancel_time_selection();
-       }
+//not sure what this was for;  remove it for now.
+//     if (UIConfiguration::instance().get_follow_edits() && (!_session || !_session->config.get_external_sync())) {
+//             cancel_time_selection();
+//     }
+
 }
 
 void
@@ -6214,18 +6296,16 @@ Editor::split_region ()
        if (current_mouse_mode() == MouseObject) {  //don't try this for Internal Edit, Stretch, Draw, etc.
 
                RegionSelection rs = get_regions_from_selection_and_edit_point ();
-
-               framepos_t where = get_preferred_edit_position ();
+               const framepos_t pos = get_preferred_edit_position();
+               const int32_t division = get_grid_music_divisions (0);
+               MusicFrame where (pos, division);
 
                if (rs.empty()) {
                        return;
                }
 
-               if (snap_musical()) {
-                       split_regions_at (where, rs, get_grid_music_divisions (0));
-               } else {
-                       split_regions_at (where, rs, 0);
-               }
+               split_regions_at (where, rs);
+
        }
 }
 
@@ -6344,6 +6424,59 @@ Editor::set_punch_from_selection ()
        set_punch_range (start, end,  _("set punch range from selection"));
 }
 
+void
+Editor::set_auto_punch_range ()
+{
+       // auto punch in/out button from a single button
+       // If Punch In is unset, set punch range from playhead to end, enable punch in
+       // If Punch In is set, the next punch sets Punch Out, unless the playhead has been
+       //   rewound beyond the Punch In marker, in which case that marker will be moved back
+       //   to the current playhead position.
+       // If punch out is set, it clears the punch range and Punch In/Out buttons
+
+       if (_session == 0) {
+               return;
+       }
+
+       Location* tpl = transport_punch_location();
+       framepos_t now = playhead_cursor->current_frame();
+       framepos_t begin = now;
+       framepos_t end = _session->current_end_frame();
+
+       if (!_session->config.get_punch_in()) {
+               // First Press - set punch in and create range from here to eternity
+               set_punch_range (begin, end, _("Auto Punch In"));
+               _session->config.set_punch_in(true);
+       } else if (tpl && !_session->config.get_punch_out()) {
+               // Second press - update end range marker and set punch_out
+               if (now < tpl->start()) {
+                       // playhead has been rewound - move start back  and pretend nothing happened
+                       begin = now;
+                       set_punch_range (begin, end, _("Auto Punch In/Out"));
+               } else {
+                       // normal case for 2nd press - set the punch out
+                       end = playhead_cursor->current_frame ();
+                       set_punch_range (tpl->start(), now, _("Auto Punch In/Out"));
+                       _session->config.set_punch_out(true);
+               }
+       } else  {
+               if (_session->config.get_punch_out()) {
+                       _session->config.set_punch_out(false);
+               }
+
+               if (_session->config.get_punch_in()) {
+                       _session->config.set_punch_in(false);
+               }
+
+               if (tpl)
+               {
+                       // third press - unset punch in/out and remove range
+                       _session->locations()->remove(tpl);
+               }
+       }
+
+}
+
 void
 Editor::set_session_extents_from_selection ()
 {
@@ -6380,7 +6513,7 @@ Editor::set_punch_start_from_edit_point ()
 {
        if (_session) {
 
-               framepos_t start = 0;
+               MusicFrame start (0, 0);
                framepos_t end = max_framepos;
 
                //use the existing punch end, if any
@@ -6390,20 +6523,20 @@ Editor::set_punch_start_from_edit_point ()
                }
 
                if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
-                       start = _session->audible_frame();
+                       start.frame = _session->audible_frame();
                } else {
-                       start = get_preferred_edit_position();
+                       start.frame = 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 > end ) {
+               if (start.frame > end ) {
                        end = max_framepos;
                }
 
-               set_punch_range (start, end, _("set punch start from EP"));
+               set_punch_range (start.frame, end, _("set punch start from EP"));
        }
 
 }
@@ -6414,7 +6547,7 @@ Editor::set_punch_end_from_edit_point ()
        if (_session) {
 
                framepos_t start = 0;
-               framepos_t end = max_framepos;
+               MusicFrame end (max_framepos, 0);
 
                //use the existing punch start, if any
                Location* tpl = transport_punch_location();
@@ -6423,15 +6556,15 @@ Editor::set_punch_end_from_edit_point ()
                }
 
                if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
-                       end = _session->audible_frame();
+                       end.frame = _session->audible_frame();
                } else {
-                       end = get_preferred_edit_position();
+                       end.frame = get_preferred_edit_position();
                }
 
                //snap the selection start/end
-               snap_to(end);
+               snap_to (end);
 
-               set_punch_range (start, end, _("set punch end from EP"));
+               set_punch_range (start, end.frame, _("set punch end from EP"));
 
        }
 }
@@ -6441,7 +6574,7 @@ Editor::set_loop_start_from_edit_point ()
 {
        if (_session) {
 
-               framepos_t start = 0;
+               MusicFrame start (0, 0);
                framepos_t end = max_framepos;
 
                //use the existing loop end, if any
@@ -6451,20 +6584,20 @@ Editor::set_loop_start_from_edit_point ()
                }
 
                if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
-                       start = _session->audible_frame();
+                       start.frame = _session->audible_frame();
                } else {
-                       start = get_preferred_edit_position();
+                       start.frame = get_preferred_edit_position();
                }
 
                //snap the selection start/end
-               snap_to(start);
+               snap_to (start);
 
                //if there's not already a sensible selection endpoint, go "forever"
-               if ( start > end ) {
+               if (start.frame > end ) {
                        end = max_framepos;
                }
 
-               set_loop_range (start, end, _("set loop start from EP"));
+               set_loop_range (start.frame, end, _("set loop start from EP"));
        }
 
 }
@@ -6475,7 +6608,7 @@ Editor::set_loop_end_from_edit_point ()
        if (_session) {
 
                framepos_t start = 0;
-               framepos_t end = max_framepos;
+               MusicFrame end (max_framepos, 0);
 
                //use the existing loop start, if any
                Location* tpl = transport_loop_location();
@@ -6484,15 +6617,15 @@ Editor::set_loop_end_from_edit_point ()
                }
 
                if ((_edit_point == EditAtPlayhead) && _session->transport_rolling()) {
-                       end = _session->audible_frame();
+                       end.frame = _session->audible_frame();
                } else {
-                       end = get_preferred_edit_position();
+                       end.frame = get_preferred_edit_position();
                }
 
                //snap the selection start/end
                snap_to(end);
 
-               set_loop_range (start, end, _("set loop end from EP"));
+               set_loop_range (start, end.frame, _("set loop end from EP"));
        }
 }
 
@@ -6625,12 +6758,13 @@ Editor::define_one_bar (framepos_t start, framepos_t end)
        XMLNode& before (_session->tempo_map().get_state());
 
        if (do_global) {
-               _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type());
+               _session->tempo_map().change_initial_tempo (beats_per_minute, t.note_type(), t.end_note_types_per_minute());
        } else if (t.frame() == start) {
-               _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type());
+               _session->tempo_map().change_existing_tempo_at (start, beats_per_minute, t.note_type(), t.end_note_types_per_minute());
        } else {
+               /* constant tempo */
                const Tempo tempo (beats_per_minute, t.note_type());
-               _session->tempo_map().add_tempo (tempo, 0.0, start, TempoSection::Constant, AudioTime);
+               _session->tempo_map().add_tempo (tempo, 0.0, start, AudioTime);
        }
 
        XMLNode& after (_session->tempo_map().get_state());
@@ -6906,10 +7040,12 @@ Editor::snap_regions_to_grid ()
                        used_playlists.push_back(pl);
                        pl->freeze();
                }
+               (*r)->region()->clear_changes ();
 
-               framepos_t start_frame = (*r)->region()->first_frame ();
-               snap_to (start_frame);
-               (*r)->region()->set_position (start_frame);
+               MusicFrame start ((*r)->region()->first_frame (), 0);
+               snap_to (start);
+               (*r)->region()->set_position (start.frame, start.division);
+               _session->add_command(new StatefulDiffCommand ((*r)->region()));
        }
 
        while (used_playlists.size() > 0) {
@@ -7003,11 +7139,16 @@ Editor::close_region_gaps ()
                        continue;
                }
 
+               (*r)->region()->clear_changes ();
                (*r)->region()->trim_front( (position - pull_back_frames));
+
+               last_region->clear_changes ();
                last_region->trim_end( (position - pull_back_frames + crossfade_len));
 
-               last_region = (*r)->region();
+               _session->add_command (new StatefulDiffCommand ((*r)->region()));
+               _session->add_command (new StatefulDiffCommand (last_region));
 
+               last_region = (*r)->region();
                idx++;
        }
 
@@ -7107,11 +7248,12 @@ Editor::playhead_forward_to_grid ()
                return;
        }
 
-       framepos_t pos = playhead_cursor->current_frame ();
-       if (pos < max_framepos - 1) {
-               pos += 2;
+       MusicFrame pos (playhead_cursor->current_frame (), 0);
+
+       if (pos.frame < max_framepos - 1) {
+               pos.frame += 2;
                snap_to_internal (pos, RoundUpAlways, false);
-               _session->request_locate (pos);
+               _session->request_locate (pos.frame);
        }
 }
 
@@ -7123,11 +7265,12 @@ Editor::playhead_backward_to_grid ()
                return;
        }
 
-       framepos_t pos = playhead_cursor->current_frame ();
-       if (pos > 2) {
-               pos -= 2;
+       MusicFrame pos  (playhead_cursor->current_frame (), 0);
+
+       if (pos.frame > 2) {
+               pos.frame -= 2;
                snap_to_internal (pos, RoundDownAlways, false);
-               _session->request_locate (pos);
+               _session->request_locate (pos.frame);
        }
 }
 
@@ -7281,8 +7424,28 @@ edit your ardour.rc file to set the\n\
                return;
        }
 
+       if (current_mixer_strip && routes.size () > 1 && std::find (routes.begin(), routes.end(), current_mixer_strip->route()) != routes.end ()) {
+               /* 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() ).
+                * It's likewise likely that the route that has just been displayed in the
+                * Editor-Mixer will be next in line for deletion.
+                *
+                * So simply switch to the master-bus (if present)
+                */
+               for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
+                       if ((*i)->stripable ()->is_master ()) {
+                               set_selected_mixer_strip (*(*i));
+                               break;
+                       }
+               }
+       }
+
        {
+               PresentationInfo::ChangeSuspender cs;
                DisplaySuspender ds;
+
                boost::shared_ptr<RouteList> rl (new RouteList);
                for (vector<boost::shared_ptr<Route> >::iterator x = routes.begin(); x != routes.end(); ++x) {
                        rl->push_back (*x);
@@ -7314,7 +7477,7 @@ Editor::do_insert_time ()
        }
 
        insert_time (
-               get_preferred_edit_position (EDIT_IGNORE_MOUSE),
+               d.position(),
                d.distance(),
                d.intersected_region_action (),
                d.all_playlists(),
@@ -7370,17 +7533,18 @@ Editor::insert_time (
                        (*i)->clear_changes ();
                        (*i)->clear_owned_changes ();
 
+                       if (!in_command) {
+                               begin_reversible_command (_("insert time"));
+                               in_command = true;
+                       }
+
                        if (opt == SplitIntersected) {
                                /* non musical split */
-                               (*i)->split (pos, 0);
+                               (*i)->split (MusicFrame (pos, 0));
                        }
 
                        (*i)->shift (pos, frames, (opt == MoveIntersected), ignore_music_glue);
 
-                       if (!in_command) {
-                               begin_reversible_command (_("insert time"));
-                               in_command = true;
-                       }
                        vector<Command*> cmds;
                        (*i)->rdiff (cmds);
                        _session->add_commands (cmds);
@@ -7402,6 +7566,7 @@ Editor::insert_time (
        /* markers */
        if (markers_too) {
                bool moved = false;
+               const int32_t divisions = get_grid_music_divisions (0);
                XMLNode& before (_session->locations()->get_state());
                Locations::LocationList copy (_session->locations()->list());
 
@@ -7418,9 +7583,9 @@ Editor::insert_time (
                                if ((*i)->start() >= pos) {
                                        // move end first, in case we're moving by more than the length of the range
                                        if (!(*i)->is_mark()) {
-                                               (*i)->set_end ((*i)->end() + frames);
+                                               (*i)->set_end ((*i)->end() + frames, false, true, divisions);
                                        }
-                                       (*i)->set_start ((*i)->start() + frames);
+                                       (*i)->set_start ((*i)->start() + frames, false, true, divisions);
                                        moved = true;
                                }
 
@@ -7463,7 +7628,6 @@ Editor::do_remove_time ()
                return;
        }
 
-       framepos_t pos = get_preferred_edit_position (EDIT_IGNORE_MOUSE);
        InsertRemoveTimeDialog d (*this, true);
 
        int response = d.run ();
@@ -7479,7 +7643,7 @@ Editor::do_remove_time ()
        }
 
        remove_time (
-               pos,
+               d.position(),
                distance,
                SplitIntersected,
                d.move_glued(),
@@ -7508,16 +7672,17 @@ Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
 
                        XMLNode &before = pl->get_state();
 
+                       if (!in_command) {
+                               begin_reversible_command (_("remove time"));
+                               in_command = true;
+                       }
+
                        std::list<AudioRange> rl;
                        AudioRange ar(pos, pos+frames, 0);
                        rl.push_back(ar);
                        pl->cut (rl);
                        pl->shift (pos, -frames, true, ignore_music_glue);
 
-                       if (!in_command) {
-                               begin_reversible_command (_("remove time"));
-                               in_command = true;
-                       }
                        XMLNode &after = pl->get_state();
 
                        _session->add_command (new MementoCommand<Playlist> (*pl, &before, &after));
@@ -7534,6 +7699,7 @@ Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
                }
        }
 
+       const int32_t divisions = get_grid_music_divisions (0);
        std::list<Location*> loc_kill_list;
 
        /* markers */
@@ -7562,20 +7728,20 @@ Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
                                                // if we're removing more time than the length of the range
                                                if ((*i)->start() >= pos && (*i)->start() < pos+frames) {
                                                        // start is within cut
-                                                       (*i)->set_start (pos);  // bring the start marker to the beginning of the cut
+                                                       (*i)->set_start (pos, false, true,divisions);  // bring the start marker to the beginning of the cut
                                                        moved = true;
                                                } else if ((*i)->start() >= pos+frames) {
                                                        // start (and thus entire range) lies beyond end of cut
-                                                       (*i)->set_start ((*i)->start() - frames); // slip the start marker back
+                                                       (*i)->set_start ((*i)->start() - frames, false, true, divisions); // slip the start marker back
                                                        moved = true;
                                                }
                                                if ((*i)->end() >= pos && (*i)->end() < pos+frames) {
                                                        // end is inside cut
-                                                       (*i)->set_end (pos);  // bring the end to the cut
+                                                       (*i)->set_end (pos, false, true, divisions);  // bring the end to the cut
                                                        moved = true;
                                                } else if ((*i)->end() >= pos+frames) {
                                                        // end is beyond end of cut
-                                                       (*i)->set_end ((*i)->end() - frames); // slip the end marker back
+                                                       (*i)->set_end ((*i)->end() - frames, false, true, divisions); // slip the end marker back
                                                        moved = true;
                                                }
 
@@ -7584,7 +7750,7 @@ Editor::remove_time (framepos_t pos, framecnt_t frames, InsertTimeOption opt,
                                        loc_kill_list.push_back(*i);
                                        moved = true;
                                } else if ((*i)->start() >= pos) {
-                                       (*i)->set_start ((*i)->start() -frames);
+                                       (*i)->set_start ((*i)->start() -frames, false, true, divisions);
                                        moved = true;
                                }
 
@@ -7922,6 +8088,8 @@ Editor::toggle_midi_input_active (bool flip_others)
        _session->set_exclusive_input_active (rl, onoff, flip_others);
 }
 
+static bool ok_fine (GdkEventAny*) { return true; }
+
 void
 Editor::lock ()
 {
@@ -7930,6 +8098,7 @@ Editor::lock ()
 
                Gtk::Image* padlock = manage (new Gtk::Image (ARDOUR_UI_UTILS::get_icon ("padlock_closed")));
                lock_dialog->get_vbox()->pack_start (*padlock);
+               lock_dialog->signal_delete_event ().connect (sigc::ptr_fun (ok_fine));
 
                ArdourButton* b = manage (new ArdourButton);
                b->set_name ("lock button");
@@ -7945,6 +8114,8 @@ Editor::lock ()
        _main_menu_disabler = new MainMenuDisabler;
 
        lock_dialog->present ();
+
+       lock_dialog->get_window()->set_decorations (Gdk::WMDecoration (0));
 }
 
 void