fix 1024px width layout (remove nudge clock special case)
[ardour.git] / gtk2_ardour / editor.cc
index c8a50a1e25b4d36aa6e3a8eb98d8bc33475b709d..534dd26ddbbce551b7638db58fabdf9b51a13a65 100644 (file)
@@ -68,6 +68,7 @@
 #include "ardour/audio_track.h"
 #include "ardour/audioengine.h"
 #include "ardour/audioregion.h"
+#include "ardour/lmath.h"
 #include "ardour/location.h"
 #include "ardour/profile.h"
 #include "ardour/route_group.h"
@@ -80,7 +81,6 @@
 
 #include "control_protocol/control_protocol.h"
 
-#include "actions.h"
 #include "actions.h"
 #include "analysis_window.h"
 #include "audio_clock.h"
 #include "gui_thread.h"
 #include "keyboard.h"
 #include "marker.h"
+#include "midi_region_view.h"
 #include "midi_time_axis.h"
 #include "mixer_strip.h"
 #include "mixer_ui.h"
 #include "mouse_cursors.h"
+#include "note_base.h"
 #include "playlist_selector.h"
 #include "public_editor.h"
 #include "region_layering_order_editor.h"
 #include "sfdb_ui.h"
 #include "tempo_lines.h"
 #include "time_axis_view.h"
+#include "timers.h"
 #include "utils.h"
 #include "verbose_cursor.h"
 
@@ -138,7 +141,7 @@ using PBD::internationalize;
 using PBD::atoi;
 using Gtkmm2ext::Keyboard;
 
-const double Editor::timebar_height = 15.0;
+double Editor::timebar_height = 15.0;
 
 static const gchar *_snap_type_strings[] = {
        N_("CD Frames"),
@@ -248,6 +251,7 @@ pane_size_watcher (Paned* pane)
 Editor::Editor ()
        : _join_object_range_state (JOIN_OBJECT_RANGE_NONE)
 
+       , _mouse_changed_selection (false)
          /* time display buttons */
        , minsec_label (_("Mins:Secs"))
        , bbt_label (_("Bars:Beats"))
@@ -281,6 +285,8 @@ Editor::Editor ()
        , _tools_tearoff (0)
 
        , _toolbar_viewport (*manage (new Gtk::Adjustment (0, 0, 1e10)), *manage (new Gtk::Adjustment (0, 0, 1e10)))
+       , selection_op_cmd_depth (0)
+       , selection_op_history_it (0)
 
          /* nudge */
 
@@ -306,6 +312,7 @@ Editor::Editor ()
        selection = new Selection (this);
        cut_buffer = new Selection (this);
        _selection_memento = new SelectionMemento ();
+       selection_op_history.clear();
        before.clear();
 
        clicked_regionview = 0;
@@ -373,7 +380,6 @@ Editor::Editor ()
        current_stepping_trackview = 0;
        entered_track = 0;
        entered_regionview = 0;
-       _entered_item_type = NoItem;
        entered_marker = 0;
        clear_entered_track = false;
        current_timefx = 0;
@@ -398,12 +404,16 @@ Editor::Editor ()
        location_loop_color = ARDOUR_UI::config()->color ("location loop");
        location_punch_color = ARDOUR_UI::config()->color ("location punch");
 
-       zoom_focus = ZoomFocusLeft;
+       zoom_focus = ZoomFocusPlayhead;
        _edit_point = EditAtMouse;
        _visible_track_count = -1;
 
        samples_per_pixel = 2048; /* too early to use reset_zoom () */
 
+       timebar_height = std::max(12., ceil (15. * ARDOUR_UI::ui_scale));
+       TimeAxisView::setup_sizes ();
+       Marker::setup_sizes (timebar_height);
+
        _scroll_callbacks = 0;
 
        bbt_label.set_name ("EditorRulerLabel");
@@ -671,7 +681,6 @@ Editor::Editor ()
        _snap_mode = SnapOff;
        set_snap_mode (_snap_mode);
        set_mouse_mode (MouseObject, true);
-        pre_internal_mouse_mode = MouseObject;
         pre_internal_snap_type = _snap_type;
         pre_internal_snap_mode = _snap_mode;
         internal_snap_type = _snap_type;
@@ -686,10 +695,10 @@ Editor::Editor ()
        /* nudge stuff */
 
        nudge_forward_button.set_name ("nudge button");
-       nudge_forward_button.set_image(::get_icon("nudge_right"));
+       nudge_forward_button.set_icon(ArdourIcon::NudgeRight);
 
        nudge_backward_button.set_name ("nudge button");
-       nudge_backward_button.set_image(::get_icon("nudge_left"));
+       nudge_backward_button.set_icon(ArdourIcon::NudgeLeft);
 
        fade_context_menu.set_name ("ArdourContextMenu");
 
@@ -1377,7 +1386,7 @@ Editor::set_session (Session *t)
                (static_cast<TimeAxisView*>(*i))->set_samples_per_pixel (samples_per_pixel);
        }
 
-       super_rapid_screen_update_connection = ARDOUR_UI::instance()->SuperRapidScreenUpdate.connect (
+       super_rapid_screen_update_connection = Timers::super_rapid_connect (
                sigc::mem_fun (*this, &Editor::super_rapid_screen_update)
                );
 
@@ -1787,10 +1796,18 @@ Editor::add_region_context_items (Menu_Helpers::MenuList& edit_items, boost::sha
                _popup_region_menu_item->set_label (menu_item_name);
        }
 
-       const framepos_t position = get_preferred_edit_position (false, true);
+       /* No latering allowed in later is higher layering model */
+       RefPtr<Action> act = ActionManager::get_action (X_("EditorMenu"), X_("RegionMenuLayering"));
+       if (act && Config->get_layer_model() == LaterHigher) {
+               act->set_sensitive (false);
+       } else if (act) {
+               act->set_sensitive (true);
+       }
+
+       const framepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, true);
 
        edit_items.push_back (*_popup_region_menu_item);
-       if (track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
+       if (Config->get_layer_model() == Manual && track->playlist()->count_regions_at (position) > 1 && (layering_order_editor == 0 || !layering_order_editor->is_visible ())) {
                edit_items.push_back (*manage (_region_actions->get_action ("choose-top-region-context-menu")->create_menu_item ()));
        }
        edit_items.push_back (SeparatorElem());
@@ -1844,16 +1861,16 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
                );
 
        edit_items.push_back (SeparatorElem());
-       edit_items.push_back (MenuElem (_("Convert to Region In-Place"), mem_fun(*this, &Editor::separate_region_from_selection)));
+       edit_items.push_back (MenuElem (_("Separate"), mem_fun(*this, &Editor::separate_region_from_selection)));
        edit_items.push_back (MenuElem (_("Convert to Region in Region List"), sigc::mem_fun(*this, &Editor::new_region_from_selection)));
 
        edit_items.push_back (SeparatorElem());
        edit_items.push_back (MenuElem (_("Select All in Range"), sigc::mem_fun(*this, &Editor::select_all_selectables_using_time_selection)));
 
        edit_items.push_back (SeparatorElem());
-       edit_items.push_back (MenuElem (_("Set Loop from Range"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
-       edit_items.push_back (MenuElem (_("Set Punch from Range"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
-       edit_items.push_back (MenuElem (_("Set Session Start/End from Range"), sigc::mem_fun(*this, &Editor::set_session_extents_from_selection)));
+       edit_items.push_back (MenuElem (_("Set Loop from Selection"), sigc::bind (sigc::mem_fun(*this, &Editor::set_loop_from_selection), false)));
+       edit_items.push_back (MenuElem (_("Set Punch from Selection"), sigc::mem_fun(*this, &Editor::set_punch_from_selection)));
+       edit_items.push_back (MenuElem (_("Set Session Start/End from Selection"), sigc::mem_fun(*this, &Editor::set_session_extents_from_selection)));
 
        edit_items.push_back (SeparatorElem());
        edit_items.push_back (MenuElem (_("Add Range Markers"), sigc::mem_fun (*this, &Editor::add_location_from_selection)));
@@ -1870,7 +1887,7 @@ Editor::add_selection_context_items (Menu_Helpers::MenuList& edit_items)
        edit_items.push_back (MenuElem (_("Bounce Range to Region List With Processing"), sigc::bind (sigc::mem_fun(*this, &Editor::bounce_range_selection), false, true)));
        edit_items.push_back (MenuElem (_("Export Range..."), sigc::mem_fun(*this, &Editor::export_selection)));
        if (ARDOUR_UI::instance()->video_timeline->get_duration() > 0) {
-               edit_items.push_back (MenuElem (_("Export Video Range..."), sigc::bind (sigc::mem_fun(*this, &Editor::export_video), true)));
+               edit_items.push_back (MenuElem (_("Export Video Range..."), sigc::bind (sigc::mem_fun(*(ARDOUR_UI::instance()), &ARDOUR_UI::export_video), true)));
        }
 }
 
@@ -1907,6 +1924,7 @@ Editor::add_dstream_context_items (Menu_Helpers::MenuList& edit_items)
        select_items.push_back (SeparatorElem());
        select_items.push_back (MenuElem (_("Set Range to Loop Range"), sigc::mem_fun(*this, &Editor::set_selection_from_loop)));
        select_items.push_back (MenuElem (_("Set Range to Punch Range"), sigc::mem_fun(*this, &Editor::set_selection_from_punch)));
+       select_items.push_back (MenuElem (_("Set Range to Selected Regions"), sigc::mem_fun(*this, &Editor::set_selection_from_region)));
        select_items.push_back (SeparatorElem());
        select_items.push_back (MenuElem (_("Select All After Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), true)));
        select_items.push_back (MenuElem (_("Select All Before Edit Point"), sigc::bind (sigc::mem_fun(*this, &Editor::select_all_selectables_using_edit), false)));
@@ -2029,6 +2047,12 @@ Editor::set_snap_to (SnapType st)
 {
        unsigned int snap_ind = (unsigned int)st;
 
+       if (internal_editing()) {
+               internal_snap_type = st;
+       } else {
+               pre_internal_snap_type = st;
+       }
+
        _snap_type = st;
 
        if (snap_ind > snap_type_strings.size() - 1) {
@@ -2085,6 +2109,8 @@ Editor::set_snap_to (SnapType st)
                break;
        }
 
+       redisplay_tempo (false);
+
        SnapChanged (); /* EMIT SIGNAL */
 }
 
@@ -2107,6 +2133,7 @@ Editor::set_snap_mode (SnapMode mode)
 
        instant_save ();
 }
+
 void
 Editor::set_edit_point_preference (EditPoint ep, bool force)
 {
@@ -2122,9 +2149,7 @@ Editor::set_edit_point_preference (EditPoint ep, bool force)
                edit_point_selector.set_text (str);
        }
 
-       if (_entered_item_type != NoItem) {
-               choose_canvas_cursor_on_entry (_entered_item_type);
-       }
+       update_all_enter_cursors();
 
        if (!force && !changed) {
                return;
@@ -2307,37 +2332,16 @@ Editor::set_state (const XMLNode& node, int /*version*/)
        if ((prop = node.property ("show-measures"))) {
                bool yn = string_is_affirmative (prop->value());
                _show_measures = yn;
-               RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
-               if (act) {
-                       RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
-                       /* do it twice to force the change */
-                       tact->set_active (!yn);
-                       tact->set_active (yn);
-               }
        }
 
        if ((prop = node.property ("follow-playhead"))) {
                bool yn = string_is_affirmative (prop->value());
                set_follow_playhead (yn);
-               RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
-               if (act) {
-                       RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
-                       if (tact->get_active() != yn) {
-                               tact->set_active (yn);
-                       }
-               }
        }
 
        if ((prop = node.property ("stationary-playhead"))) {
                bool yn = string_is_affirmative (prop->value());
                set_stationary_playhead (yn);
-               RefPtr<Action> act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
-               if (act) {
-                       RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
-                       if (tact->get_active() != yn) {
-                               tact->set_active (yn);
-                       }
-               }
        }
 
        if ((prop = node.property ("region-list-sort-type"))) {
@@ -2413,6 +2417,42 @@ Editor::set_state (const XMLNode& node, int /*version*/)
                nudge_clock->set (_session->frame_rate() * 5, true);
        }
 
+       {
+               /* apply state
+                * Not all properties may have been in XML, but
+                * those that are linked to a private variable may need changing
+                */
+               RefPtr<Action> act;
+               bool yn;
+
+               act = ActionManager::get_action (X_("Editor"), X_("ToggleMeasureVisibility"));
+               if (act) {
+                       yn = _show_measures;
+                       RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
+                       /* do it twice to force the change */
+                       tact->set_active (!yn);
+                       tact->set_active (yn);
+               }
+
+               act = ActionManager::get_action (X_("Editor"), X_("toggle-follow-playhead"));
+               yn = _follow_playhead;
+               if (act) {
+                       RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
+                       if (tact->get_active() != yn) {
+                               tact->set_active (yn);
+                       }
+               }
+
+               act = ActionManager::get_action (X_("Editor"), X_("toggle-stationary-playhead"));
+               yn = _stationary_playhead;
+               if (act) {
+                       RefPtr<ToggleAction> tact = RefPtr<ToggleAction>::cast_dynamic(act);
+                       if (tact->get_active() != yn) {
+                               tact->set_active (yn);
+                       }
+               }
+       }
+
        return 0;
 }
 
@@ -2557,25 +2597,28 @@ Editor::snap_to_with_modifier (framepos_t& start, GdkEvent const * event, RoundM
                return;
        }
 
-       if (Keyboard::modifier_state_contains (event->button.state, Keyboard::snap_modifier())) {
+       if (ArdourKeyboard::indicates_snap (event->button.state)) {
                if (_snap_mode == SnapOff) {
                        snap_to_internal (start, direction, for_mark);
                }
        } else {
                if (_snap_mode != SnapOff) {
                        snap_to_internal (start, direction, for_mark);
+               } else if (ArdourKeyboard::indicates_snap_delta (event->button.state)) {
+                       /* SnapOff, but we pressed the snap_delta modifier */
+                       snap_to_internal (start, direction, for_mark);
                }
        }
 }
 
 void
-Editor::snap_to (framepos_t& start, RoundMode direction, bool for_mark)
+Editor::snap_to (framepos_t& start, RoundMode direction, bool for_mark, bool ensure_snap)
 {
-       if (!_session || _snap_mode == SnapOff) {
+       if (!_session || (_snap_mode == SnapOff && !ensure_snap)) {
                return;
        }
 
-       snap_to_internal (start, direction, for_mark);
+       snap_to_internal (start, direction, for_mark, ensure_snap);
 }
 
 void
@@ -2645,7 +2688,7 @@ Editor::timecode_snap_to_internal (framepos_t& start, RoundMode direction, bool
 }
 
 void
-Editor::snap_to_internal (framepos_t& start, RoundMode direction, bool for_mark)
+Editor::snap_to_internal (framepos_t& start, RoundMode direction, bool for_mark, bool ensure_snap)
 {
        const framepos_t one_second = _session->frame_rate();
        const framepos_t one_minute = _session->frame_rate() * 60;
@@ -2815,6 +2858,10 @@ Editor::snap_to_internal (framepos_t& start, RoundMode direction, bool for_mark)
 
        case SnapMagnetic:
 
+               if (ensure_snap) {
+                       return;
+               }
+
                if (presnap > start) {
                        if (presnap > (start + pixel_to_sample(snap_threshold))) {
                                start = presnap;
@@ -2939,17 +2986,17 @@ Editor::setup_toolbar ()
        zoom_preset_selector.set_size_request (42, -1);
 
        zoom_in_button.set_name ("zoom button");
-       zoom_in_button.set_image(::get_icon ("zoom_in"));
+       zoom_in_button.set_icon (ArdourIcon::ZoomIn);
        act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-in"));
        zoom_in_button.set_related_action (act);
 
        zoom_out_button.set_name ("zoom button");
-       zoom_out_button.set_image(::get_icon ("zoom_out"));
+       zoom_out_button.set_icon (ArdourIcon::ZoomOut);
        act = ActionManager::get_action (X_("Editor"), X_("temporal-zoom-out"));
        zoom_out_button.set_related_action (act);
 
        zoom_out_full_button.set_name ("zoom button");
-       zoom_out_full_button.set_image(::get_icon ("zoom_full"));
+       zoom_out_full_button.set_icon (ArdourIcon::ZoomFull);
        act = ActionManager::get_action (X_("Editor"), X_("zoom-to-session"));
        zoom_out_full_button.set_related_action (act);
 
@@ -2977,12 +3024,12 @@ Editor::setup_toolbar ()
        }
 
        tav_expand_button.set_name ("zoom button");
-       tav_expand_button.set_image(::get_icon ("tav_exp"));
+       tav_expand_button.set_icon (ArdourIcon::TimeAxisExpand);
        act = ActionManager::get_action (X_("Editor"), X_("expand-tracks"));
        tav_expand_button.set_related_action (act);
 
        tav_shrink_button.set_name ("zoom button");
-       tav_shrink_button.set_image(::get_icon ("tav_shrink"));
+       tav_shrink_button.set_icon (ArdourIcon::TimeAxisShrink);
        act = ActionManager::get_action (X_("Editor"), X_("shrink-tracks"));
        tav_shrink_button.set_related_action (act);
 
@@ -3074,11 +3121,7 @@ Editor::setup_toolbar ()
 
        if (!ARDOUR::Profile->get_trx()) {
                hbox->pack_start (snap_box, false, false);
-               if ( !Profile->get_small_screen() || Profile->get_mixbus() ) {
-                       hbox->pack_start (*nudge_box, false, false);
-               } else {
-                       ARDOUR_UI::instance()->editor_transport_box().pack_start (*nudge_box, false, false);
-               }
+               hbox->pack_start (*nudge_box, false, false);
        }
        hbox->pack_start (panic_box, false, false);
 
@@ -3177,14 +3220,14 @@ Editor::build_snap_type_menu ()
 void
 Editor::setup_tooltips ()
 {
-       ARDOUR_UI::instance()->set_tip (smart_mode_button, _("Smart Mode (add Range functions to Object mode)"));
-       ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Object Mode (select/move Objects)"));
-       ARDOUR_UI::instance()->set_tip (mouse_cut_button, _("Cut Mode (split Regions)"));
-       ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Grab/Select Objects"));
-       ARDOUR_UI::instance()->set_tip (mouse_draw_button, _("Draw/Edit Gain/Notes/Automation"));
-       ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch/Shrink Regions and MIDI Notes"));
-       ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Listen to Specific Regions"));
-       ARDOUR_UI::instance()->set_tip (mouse_content_button, _("Edit Contents (notes and automation)"));
+       ARDOUR_UI::instance()->set_tip (smart_mode_button, _("Smart Mode (add Range functions to Grab mode)"));
+       ARDOUR_UI::instance()->set_tip (mouse_move_button, _("Grab Mode (select/move objects)"));
+       ARDOUR_UI::instance()->set_tip (mouse_cut_button, _("Cut Mode (split regions)"));
+       ARDOUR_UI::instance()->set_tip (mouse_select_button, _("Range Mode (select time ranges)"));
+       ARDOUR_UI::instance()->set_tip (mouse_draw_button, _("Draw Mode (draw and edit gain/notes/automation)"));
+       ARDOUR_UI::instance()->set_tip (mouse_timefx_button, _("Stretch Mode (time-stretch audio and midi regions, preserving pitch)"));
+       ARDOUR_UI::instance()->set_tip (mouse_audition_button, _("Audition Mode (listen to regions)"));
+       ARDOUR_UI::instance()->set_tip (mouse_content_button, _("Internal Edit Mode (edit notes and automation points)"));
        ARDOUR_UI::instance()->set_tip (*_group_tabs, _("Groups: click to (de)activate\nContext-click for other operations"));
        ARDOUR_UI::instance()->set_tip (nudge_forward_button, _("Nudge Region/Selection Later"));
        ARDOUR_UI::instance()->set_tip (nudge_backward_button, _("Nudge Region/Selection Earlier"));
@@ -3307,6 +3350,109 @@ Editor::map_transport_state ()
 
 /* UNDO/REDO */
 
+void
+Editor::begin_selection_op_history ()
+{
+       selection_op_cmd_depth = 0;
+       selection_op_history_it = 0;
+
+       while(!selection_op_history.empty()) {
+               delete selection_op_history.front();
+               selection_op_history.pop_front();
+       }
+
+       selection_undo_action->set_sensitive (false);
+       selection_redo_action->set_sensitive (false);
+       selection_op_history.push_front (&_selection_memento->get_state ());
+}
+
+void
+Editor::begin_reversible_selection_op (string name)
+{
+       if (_session) {
+               //cerr << name << endl;
+               /* begin/commit pairs can be nested */
+               selection_op_cmd_depth++;
+       }
+}
+
+void
+Editor::commit_reversible_selection_op ()
+{
+       if (_session) {
+               if (selection_op_cmd_depth == 1) {
+
+                       if (selection_op_history_it > 0 && selection_op_history_it < selection_op_history.size()) {
+                               /**
+                                   The user has undone some selection ops and then made a new one,
+                                   making anything earlier in the list invalid.
+                               */
+                               
+                               list<XMLNode *>::iterator it = selection_op_history.begin();
+                               list<XMLNode *>::iterator e_it = it;
+                               advance (e_it, selection_op_history_it);
+                               
+                               for ( ; it != e_it; ++it) {
+                                       delete *it;
+                               }
+                               selection_op_history.erase (selection_op_history.begin(), e_it);
+                       }
+
+                       selection_op_history.push_front (&_selection_memento->get_state ());
+                       selection_op_history_it = 0;
+
+                       selection_undo_action->set_sensitive (true);
+                       selection_redo_action->set_sensitive (false);
+               }
+
+               if (selection_op_cmd_depth > 0) {
+                       selection_op_cmd_depth--;
+               }
+       }
+}
+
+void
+Editor::undo_selection_op ()
+{
+       if (_session) {
+               selection_op_history_it++;
+               uint32_t n = 0;
+               for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
+                       if (n == selection_op_history_it) {
+                               _selection_memento->set_state (*(*i), Stateful::current_state_version);
+                               selection_redo_action->set_sensitive (true);
+                       }
+                       ++n;
+               }
+               /* is there an earlier entry? */
+               if ((selection_op_history_it + 1) >= selection_op_history.size()) {
+                       selection_undo_action->set_sensitive (false);
+               }
+       }
+}
+
+void
+Editor::redo_selection_op ()
+{
+       if (_session) {
+               if (selection_op_history_it > 0) {
+                       selection_op_history_it--;
+               }
+               uint32_t n = 0;
+               for (std::list<XMLNode *>::iterator i = selection_op_history.begin(); i != selection_op_history.end(); ++i) {
+                       if (n == selection_op_history_it) {
+                               _selection_memento->set_state (*(*i), Stateful::current_state_version);
+                               selection_undo_action->set_sensitive (true);
+                       }
+                       ++n;
+               }
+
+               if (selection_op_history_it == 0) {
+                       selection_redo_action->set_sensitive (false);
+               }
+       }
+}
+
 void
 Editor::begin_reversible_command (string name)
 {
@@ -3325,15 +3471,32 @@ Editor::begin_reversible_command (GQuark q)
        }
 }
 
+void
+Editor::abort_reversible_command ()
+{
+       if (_session) {
+               while(!before.empty()) {
+                       delete before.front();
+                       before.pop_front();
+               }
+               _session->abort_reversible_command ();
+       }
+}
+
 void
 Editor::commit_reversible_command ()
 {
        if (_session) {
                if (before.size() == 1) {
                        _session->add_command (new MementoCommand<SelectionMemento>(*(_selection_memento), before.front(), &_selection_memento->get_state ()));
+                       redo_action->set_sensitive(false);
+                       undo_action->set_sensitive(true);
+                       begin_selection_op_history ();
                }
 
-               if (!before.empty()) {
+               if (before.empty()) {
+                       cerr << "Please call begin_reversible_command() before commit_reversible_command()." << endl;
+               } else {
                        before.pop_back();
                }
 
@@ -3553,7 +3716,7 @@ Editor::build_track_count_menu ()
                visible_tracks_selector.AddMenuElem (MenuElem (X_("24"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 24)));
                visible_tracks_selector.AddMenuElem (MenuElem (X_("32"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
                visible_tracks_selector.AddMenuElem (MenuElem (X_("64"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 64)));
-               visible_tracks_selector.AddMenuElem (MenuElem (_("Selected"), sigc::mem_fun(*this, &Editor::fit_selected_tracks)));
+               visible_tracks_selector.AddMenuElem (MenuElem (_("Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
                visible_tracks_selector.AddMenuElem (MenuElem (_("All"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
        } else {
                visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 1 track"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 1)));
@@ -3565,7 +3728,7 @@ Editor::build_track_count_menu ()
                visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 32 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 32)));
                visible_tracks_selector.AddMenuElem (MenuElem (_("Fit 48 tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 48)));
                visible_tracks_selector.AddMenuElem (MenuElem (_("Fit All tracks"), sigc::bind (sigc::mem_fun(*this, &Editor::set_visible_track_count), 0)));
-               visible_tracks_selector.AddMenuElem (MenuElem (_("Fit Selected tracks"), sigc::mem_fun(*this, &Editor::fit_selected_tracks)));
+               visible_tracks_selector.AddMenuElem (MenuElem (_("Fit Selection"), sigc::mem_fun(*this, &Editor::fit_selection)));
 
                zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 10 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 10)));
                zoom_preset_selector.AddMenuElem (MenuElem (_("Zoom to 100 ms"), sigc::bind (sigc::mem_fun(*this, &Editor::set_zoom_preset), 100)));
@@ -3610,6 +3773,7 @@ Editor::set_visible_track_count (int32_t n)
 
        int h;
        string str;
+       DisplaySuspender ds;
        
        if (_visible_track_count > 0) {
                h = trackviews_height() / _visible_track_count;
@@ -3634,7 +3798,7 @@ Editor::set_visible_track_count (int32_t n)
        }
 
        for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {
-               (*i)->set_height (h);
+               (*i)->set_height (h, TimeAxisView::HeightPerLane);
        }
        
        if (str != visible_tracks_selector.get_text()) {
@@ -3900,90 +4064,56 @@ Editor::get_paste_offset (framepos_t pos, unsigned paste_count, framecnt_t durat
        return offset;
 }
 
-Evoral::MusicalTime
+unsigned
+Editor::get_grid_beat_divisions(framepos_t position)
+{
+       switch (_snap_type) {
+       case SnapToBeatDiv128: return 128;
+       case SnapToBeatDiv64:  return 64;
+       case SnapToBeatDiv32:  return 32;
+       case SnapToBeatDiv28:  return 28;
+       case SnapToBeatDiv24:  return 24;
+       case SnapToBeatDiv20:  return 20;
+       case SnapToBeatDiv16:  return 16;
+       case SnapToBeatDiv14:  return 14;
+       case SnapToBeatDiv12:  return 12;
+       case SnapToBeatDiv10:  return 10;
+       case SnapToBeatDiv8:   return 8;
+       case SnapToBeatDiv7:   return 7;
+       case SnapToBeatDiv6:   return 6;
+       case SnapToBeatDiv5:   return 5;
+       case SnapToBeatDiv4:   return 4;
+       case SnapToBeatDiv3:   return 3;
+       case SnapToBeatDiv2:   return 2;
+       default:               return 0;
+       }
+       return 0;
+}
+
+Evoral::Beats
 Editor::get_grid_type_as_beats (bool& success, framepos_t position)
 {
        success = true;
 
+       const unsigned divisions = get_grid_beat_divisions(position);
+       if (divisions) {
+               return Evoral::Beats(1.0 / (double)get_grid_beat_divisions(position));
+       }
+
        switch (_snap_type) {
        case SnapToBeat:
-               return Evoral::MusicalTime(1.0);
-               break;
-
-       case SnapToBeatDiv128:
-               return Evoral::MusicalTime(1.0/128.0);
-               break;
-       case SnapToBeatDiv64:
-               return Evoral::MusicalTime(1.0/64.0);
-               break;
-       case SnapToBeatDiv32:
-               return Evoral::MusicalTime(1.0/32.0);
-               break;
-       case SnapToBeatDiv28:
-               return Evoral::MusicalTime(1.0/28.0);
-               break;
-       case SnapToBeatDiv24:
-               return Evoral::MusicalTime(1.0/24.0);
-               break;
-       case SnapToBeatDiv20:
-               return Evoral::MusicalTime(1.0/20.0);
-               break;
-       case SnapToBeatDiv16:
-               return Evoral::MusicalTime(1.0/16.0);
-               break;
-       case SnapToBeatDiv14:
-               return Evoral::MusicalTime(1.0/14.0);
-               break;
-       case SnapToBeatDiv12:
-               return Evoral::MusicalTime(1.0/12.0);
-               break;
-       case SnapToBeatDiv10:
-               return Evoral::MusicalTime(1.0/10.0);
-               break;
-       case SnapToBeatDiv8:
-               return Evoral::MusicalTime(1.0/8.0);
-               break;
-       case SnapToBeatDiv7:
-               return Evoral::MusicalTime(1.0/7.0);
-               break;
-       case SnapToBeatDiv6:
-               return Evoral::MusicalTime(1.0/6.0);
-               break;
-       case SnapToBeatDiv5:
-               return Evoral::MusicalTime(1.0/5.0);
-               break;
-       case SnapToBeatDiv4:
-               return Evoral::MusicalTime(1.0/4.0);
-               break;
-       case SnapToBeatDiv3:
-               return Evoral::MusicalTime(1.0/3.0);
-               break;
-       case SnapToBeatDiv2:
-               return Evoral::MusicalTime(1.0/2.0);
-               break;
-
+               return Evoral::Beats(1.0);
        case SnapToBar:
                if (_session) {
-                       return Evoral::MusicalTime(_session->tempo_map().meter_at (position).divisions_per_bar());
+                       return Evoral::Beats(_session->tempo_map().meter_at (position).divisions_per_bar());
                }
                break;
-
-       case SnapToCDFrame:
-       case SnapToTimecodeFrame:
-       case SnapToTimecodeSeconds:
-       case SnapToTimecodeMinutes:
-       case SnapToSeconds:
-       case SnapToMinutes:
-       case SnapToRegionStart:
-       case SnapToRegionEnd:
-       case SnapToRegionSync:
-       case SnapToRegionBoundary:
        default:
                success = false;
                break;
        }
 
-       return Evoral::MusicalTime();
+       return Evoral::Beats();
 }
 
 framecnt_t
@@ -4360,6 +4490,7 @@ Editor::use_visual_state (VisualState& vs)
                *ARDOUR_UI::instance()->gui_object_state = *vs.gui_state;
                
                for (TrackViewList::iterator i = track_views.begin(); i != track_views.end(); ++i) {    
+                       (*i)->clear_property_cache();
                        (*i)->reset_visual_state ();
                }
        }
@@ -4537,17 +4668,19 @@ Editor::sort_track_selection (TrackViewList& sel)
 }
 
 framepos_t
-Editor::get_preferred_edit_position (bool ignore_playhead, bool from_context_menu)
+Editor::get_preferred_edit_position (EditIgnoreOption ignore, bool from_context_menu, bool from_outside_canvas)
 {
        bool ignored;
        framepos_t where = 0;
        EditPoint ep = _edit_point;
 
-       if(Profile->get_mixbus())
+       if (Profile->get_mixbus())
                if (ep == EditAtSelectedMarker)
-                       ep=EditAtPlayhead;
-               
-       if (from_context_menu && (ep == EditAtMouse)) {
+                       ep = EditAtPlayhead;
+
+       if (from_outside_canvas && (ep == EditAtMouse)) {
+               ep = EditAtPlayhead;
+       } else if (from_context_menu && (ep == EditAtMouse)) {
                return  canvas_event_sample (&context_click_event, 0, 0);
        }
 
@@ -4556,13 +4689,24 @@ Editor::get_preferred_edit_position (bool ignore_playhead, bool from_context_men
                return entered_marker->position();
        }
 
-       if (ignore_playhead && ep == EditAtPlayhead) {
+       if ( (ignore==EDIT_IGNORE_PHEAD) && ep == EditAtPlayhead) {
                ep = EditAtSelectedMarker;
        }
 
+       if ( (ignore==EDIT_IGNORE_MOUSE) && ep == EditAtMouse) {
+               ep = EditAtPlayhead;
+       }
+
        switch (ep) {
        case EditAtPlayhead:
-               where = _session->audible_frame();
+               if (_dragging_playhead) {
+                       if (!mouse_frame (where, ignored)) {
+                               /* XXX not right but what can we do ? */
+                               return 0;
+                       }
+               } else {
+                       where = _session->audible_frame();
+               }
                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("GPEP: use playhead @ %1\n", where));
                break;
 
@@ -4817,17 +4961,17 @@ Editor::get_regions_from_selection_and_entered ()
 }
 
 void
-Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const
+Editor::get_regionviews_by_id (PBD::ID const id, RegionSelection & regions) const
 {
        for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
-               RouteTimeAxisView* tatv;
+               RouteTimeAxisView* rtav;
                
-               if ((tatv = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
+               if ((rtav = dynamic_cast<RouteTimeAxisView*> (*i)) != 0) {
                        boost::shared_ptr<Playlist> pl;
                        std::vector<boost::shared_ptr<Region> > results;
                        boost::shared_ptr<Track> tr;
                        
-                       if ((tr = tatv->track()) == 0) {
+                       if ((tr = rtav->track()) == 0) {
                                /* bus */
                                continue;
                        }
@@ -4835,9 +4979,9 @@ Editor::get_regionviews_by_id (PBD::ID const & id, RegionSelection & regions) co
                        if ((pl = (tr->playlist())) != 0) {
                                boost::shared_ptr<Region> r = pl->region_by_id (id);
                                if (r) {
-                                       RegionView* marv = tatv->view()->find_view (r);
-                                       if (marv) {
-                                               regions.push_back (marv);
+                                       RegionView* rv = rtav->view()->find_view (r);
+                                       if (rv) {
+                                               regions.push_back (rv);
                                        }
                                }
                        }
@@ -4845,6 +4989,21 @@ Editor::get_regionviews_by_id (PBD::ID const & id, RegionSelection & regions) co
        }
 }
 
+void
+Editor::get_per_region_note_selection (list<pair<PBD::ID, set<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > > &selection) const
+{
+
+       for (TrackViewList::const_iterator i = track_views.begin(); i != track_views.end(); ++i) {
+               MidiTimeAxisView* mtav;
+
+               if ((mtav = dynamic_cast<MidiTimeAxisView*> (*i)) != 0) {
+
+                       mtav->get_per_region_note_selection (selection);
+               }
+       }
+       
+}
+
 void
 Editor::get_regions_corresponding_to (boost::shared_ptr<Region> region, vector<RegionView*>& regions, bool src_comparison)
 {
@@ -4917,6 +5076,13 @@ Editor::first_idle ()
        _routes->redisplay ();
 
        delete dialog;
+
+       if (_session->undo_depth() == 0) {
+               undo_action->set_sensitive(false);
+       }
+       redo_action->set_sensitive(false);
+       begin_selection_op_history ();
+
        _have_idled = true;
 }
 
@@ -5013,6 +5179,19 @@ Editor::region_view_added (RegionView * rv)
                        break;
                }
        }
+
+       MidiRegionView* mrv = dynamic_cast<MidiRegionView*> (rv);
+       if (mrv) {
+               list<pair<PBD::ID const, list<boost::shared_ptr<Evoral::Note<Evoral::Beats> > > > >::iterator rnote;
+               for (rnote = selection->pending_midi_note_selection.begin(); rnote != selection->pending_midi_note_selection.end(); ++rnote) {
+                       if (rv->region()->id () == (*rnote).first) {
+                               mrv->select_notes ((*rnote).second);
+                               selection->pending_midi_note_selection.erase(rnote);
+                               break;
+                       }
+               }
+       }
+
        _summary->set_background_dirty ();
 }
 
@@ -5065,6 +5244,7 @@ void
 Editor::resume_route_redisplay ()
 {
        if (_routes) {
+               _routes->redisplay(); // queue redisplay
                _routes->resume_redisplay();
        }
 }
@@ -5076,6 +5256,8 @@ Editor::add_routes (RouteList& routes)
 
        RouteTimeAxisView *rtv;
        list<RouteTimeAxisView*> new_views;
+       TrackViewList new_selection;
+       bool from_scratch = (track_views.size() == 0);
 
        for (RouteList::iterator x = routes.begin(); x != routes.end(); ++x) {
                boost::shared_ptr<Route> route = (*x);
@@ -5098,6 +5280,7 @@ Editor::add_routes (RouteList& routes)
 
                new_views.push_back (rtv);
                track_views.push_back (rtv);
+               new_selection.push_back (rtv);
 
                rtv->effective_gain_display ();
 
@@ -5110,6 +5293,12 @@ Editor::add_routes (RouteList& routes)
                _summary->routes_added (new_views);
        }
 
+       if (!from_scratch) {
+               selection->tracks.clear();
+               selection->add (new_selection);
+               begin_selection_op_history();
+       }
+
        if (show_editor_mixer_when_tracks_arrive) {
                show_editor_mixer (true);
        }
@@ -5495,24 +5684,15 @@ Editor::super_rapid_screen_update ()
 
                } else {
 
-                       /* don't do continuous scroll till the new position is in the rightmost quarter of the
-                          editor canvas
-                       */
-#if 0
-                       // FIXME DO SOMETHING THAT WORKS HERE - this is 2.X code
-                       double target = ((double)frame - (double)current_page_samples()/2.0) / samples_per_pixel;
-                       if (target <= 0.0) {
-                               target = 0.0;
-                       }
-                       if (fabs(target - current) < current_page_samples() / samples_per_pixel) {
-                               target = (target * 0.15) + (current * 0.85);
-                       } else {
-                               /* relax */
+                       if (!_dragging_playhead && _follow_playhead && _session->requested_return_frame() < 0 && !pending_visual_change.being_handled) {
+                               framepos_t const frame = playhead_cursor->current_frame ();
+                               double target = ((double)frame - (double)current_page_samples()/3.0);
+                               if (target <= 0.0) {
+                                       target = 0.0;
+                               }
+                               reset_x_origin (target);
                        }
 
-                       current = target;
-                       set_horizontal_position (current);
-#endif
                }
 
        }
@@ -5602,7 +5782,7 @@ Editor::show_editor_list (bool yn)
 void
 Editor::change_region_layering_order (bool from_context_menu)
 {
-       const framepos_t position = get_preferred_edit_position (false, from_context_menu);
+       const framepos_t position = get_preferred_edit_position (EDIT_IGNORE_NONE, from_context_menu);
 
        if (!clicked_routeview) {
                if (layering_order_editor) {
@@ -5737,6 +5917,42 @@ Editor::popup_control_point_context_menu (ArdourCanvas::Item* item, GdkEvent* ev
        _control_point_context_menu.popup (event->button.button, event->button.time);
 }
 
+void
+Editor::popup_note_context_menu (ArdourCanvas::Item* item, GdkEvent* event)
+{
+       using namespace Menu_Helpers;
+
+       NoteBase* note = reinterpret_cast<NoteBase*>(item->get_data("notebase"));
+       if (!note) {
+               return;
+       }
+
+       /* We need to get the selection here and pass it to the operations, since
+          popping up the menu will cause a region leave event which clears
+          entered_regionview. */
+
+       MidiRegionView&       mrv = note->region_view();
+       const RegionSelection rs  = get_regions_from_selection_and_entered ();
+
+       MenuList& items = _note_context_menu.items();
+       items.clear();
+
+       items.push_back(MenuElem(_("Delete"),
+                                sigc::mem_fun(mrv, &MidiRegionView::delete_selection)));
+       items.push_back(MenuElem(_("Edit..."),
+                                sigc::bind(sigc::mem_fun(*this, &Editor::edit_notes), &mrv)));
+       items.push_back(MenuElem(_("Legatize"),
+                                sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, false)));
+       items.push_back(MenuElem(_("Quantize..."),
+                                sigc::bind(sigc::mem_fun(*this, &Editor::quantize_regions), rs)));
+       items.push_back(MenuElem(_("Remove Overlap"),
+                                sigc::bind(sigc::mem_fun(*this, &Editor::legatize_regions), rs, true)));
+       items.push_back(MenuElem(_("Transform..."),
+                                sigc::bind(sigc::mem_fun(*this, &Editor::transform_regions), rs)));
+
+       _note_context_menu.popup (event->button.button, event->button.time);
+}
+
 void
 Editor::zoom_vertical_modifier_released()
 {