Prefer testing the session instead of using a global variable
[ardour.git] / gtk2_ardour / editor_canvas.cc
index 3439edb43e2164662f5e09b7428777ad02fc0add..2e92a374804a99ad06e6624d7296ac38b29f0d3a 100644 (file)
 #include "keyboard.h"
 #include "editor_cursors.h"
 #include "mouse_cursors.h"
+#include "note_base.h"
 #include "ui_config.h"
 #include "verbose_cursor.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -72,13 +73,14 @@ Editor::initialize_canvas ()
        _track_canvas = _track_canvas_viewport->canvas ();
 
        _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
+       _track_canvas->use_nsglview ();
 
        /* scroll group for items that should not automatically scroll
         *  (e.g verbose cursor). It shares the canvas coordinate space.
        */
        no_scroll_group = new ArdourCanvas::Container (_track_canvas->root());
 
-       ArdourCanvas::ScrollGroup* hsg; 
+       ArdourCanvas::ScrollGroup* hsg;
        ArdourCanvas::ScrollGroup* hg;
        ArdourCanvas::ScrollGroup* cg;
 
@@ -86,7 +88,7 @@ Editor::initialize_canvas ()
        CANVAS_DEBUG_NAME (h_scroll_group, "canvas h scroll");
        _track_canvas->add_scroller (*hg);
 
-       hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(), 
+       hv_scroll_group = hsg = new ArdourCanvas::ScrollGroup (_track_canvas->root(),
                                                               ArdourCanvas::ScrollGroup::ScrollSensitivity (ArdourCanvas::ScrollGroup::ScrollsVertically|
                                                                                                             ArdourCanvas::ScrollGroup::ScrollsHorizontally));
        CANVAS_DEBUG_NAME (hv_scroll_group, "canvas hv scroll");
@@ -98,18 +100,6 @@ Editor::initialize_canvas ()
 
        _verbose_cursor = new VerboseCursor (this);
 
-       /* on the bottom, an image */
-
-       if (Profile->get_sae()) {
-               Image img (::get_icon (X_("saelogo")));
-               // logo_item = new ArdourCanvas::Pixbuf (_track_canvas->root(), 0.0, 0.0, img.get_pixbuf());
-               // logo_item->property_height_in_pixels() = true;
-               // logo_item->property_width_in_pixels() = true;
-               // logo_item->property_height_set() = true;
-               // logo_item->property_width_set() = true;
-               // logo_item->show ();
-       }
-
        /*a group to hold global rects like punch/loop indicators */
        global_rect_group = new ArdourCanvas::Container (hv_scroll_group);
        CANVAS_DEBUG_NAME (global_rect_group, "global rect group");
@@ -128,7 +118,7 @@ Editor::initialize_canvas ()
 
        _trackview_group = new ArdourCanvas::Container (hv_scroll_group);
        CANVAS_DEBUG_NAME (_trackview_group, "Canvas TrackViews");
-       
+
        // used as rubberband rect
        rubberband_rect = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, 0.0, 0.0));
        rubberband_rect->hide();
@@ -136,11 +126,11 @@ Editor::initialize_canvas ()
        /* a group to hold stuff while it gets dragged around. Must be the
         * uppermost (last) group with hv_scroll_group as a parent
         */
-       _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);                                                                                                                                     
-        CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
+       _drag_motion_group = new ArdourCanvas::Container (hv_scroll_group);
+       CANVAS_DEBUG_NAME (_drag_motion_group, "Canvas Drag Motion");
 
        /* TIME BAR CANVAS */
-       
+
        _time_markers_group = new ArdourCanvas::Container (h_scroll_group);
        CANVAS_DEBUG_NAME (_time_markers_group, "time bars");
 
@@ -184,10 +174,10 @@ Editor::initialize_canvas ()
 
        cd_marker_bar = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, timebar_height));
        CANVAS_DEBUG_NAME (cd_marker_bar, "CD Marker Bar");
-       cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
+       cd_marker_bar->set_outline_what (ArdourCanvas::Rectangle::BOTTOM);
 
        ARDOUR_UI::instance()->video_timeline = new VideoTimeLine(this, videotl_group, (timebar_height * videotl_bar_height));
-       
+
        cd_marker_bar_drag_rect = new ArdourCanvas::Rectangle (cd_marker_group, ArdourCanvas::Rect (0.0, 0.0, 100, timebar_height));
        CANVAS_DEBUG_NAME (cd_marker_bar_drag_rect, "cd marker drag");
        cd_marker_bar_drag_rect->set_outline (false);
@@ -227,9 +217,7 @@ Editor::initialize_canvas ()
 
        playhead_cursor = new EditorCursor (*this, &Editor::canvas_playhead_cursor_event);
 
-       if (logo_item) {
-               logo_item->lower_to_bottom ();
-       }
+       snapped_cursor = new EditorCursor (*this);
 
        _canvas_drop_zone = new ArdourCanvas::Rectangle (hv_scroll_group, ArdourCanvas::Rect (0.0, 0.0, ArdourCanvas::COORD_MAX, 0.0));
        /* this thing is transparent */
@@ -255,6 +243,8 @@ Editor::initialize_canvas ()
        _track_canvas->signal_enter_notify_event().connect (sigc::mem_fun(*this, &Editor::entered_track_canvas), false);
        _track_canvas->set_flags (CAN_FOCUS);
 
+       _track_canvas->PreRender.connect (sigc::mem_fun(*this, &Editor::pre_render));
+
        /* set up drag-n-drop */
 
        vector<TargetEntry> target_table;
@@ -316,7 +306,7 @@ Editor::track_canvas_viewport_size_allocated ()
        }
 
        update_fixed_rulers();
-       redisplay_tempo (false);
+       redisplay_grid (false);
        _summary->set_overlays_dirty ();
 }
 
@@ -329,17 +319,17 @@ Editor::reset_controls_layout_width ()
        edit_controls_vbox.size_request (req);
        w = req.width;
 
-        if (_group_tabs->is_mapped()) {
+       if (_group_tabs->is_visible()) {
                _group_tabs->size_request (req);
-                w += req.width;
-        }
+               w += req.width;
+       }
 
-        /* the controls layout has no horizontal scrolling, its visible
-           width is always equal to the total width of its contents.
-        */
+       /* the controls layout has no horizontal scrolling, its visible
+          width is always equal to the total width of its contents.
+       */
 
-        controls_layout.property_width() = w;
-        controls_layout.property_width_request() = w;
+       controls_layout.property_width() = w;
+       controls_layout.property_width_request() = w;
 }
 
 void
@@ -357,11 +347,11 @@ Editor::reset_controls_layout_height (int32_t h)
 
        h += _canvas_drop_zone->height ();
 
-        /* set the height of the scrollable area (i.e. the sum of all contained widgets)
+       /* set the height of the scrollable area (i.e. the sum of all contained widgets)
         * for the controls layout. The size request is set elsewhere.
-         */
+        */
 
-        controls_layout.property_height() = h;
+       controls_layout.property_height() = h;
 
 }
 
@@ -383,6 +373,9 @@ Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context
                                         const SelectionData& data,
                                         guint info, guint time)
 {
+       if (!ARDOUR_UI_UTILS::engine_is_running ()) {
+               return;
+       }
        if (data.get_target() == "regions") {
                drop_regions (context, x, y, data, info, time);
        } else {
@@ -391,17 +384,17 @@ Editor::track_canvas_drag_data_received (const RefPtr<Gdk::DragContext>& context
 }
 
 bool
-Editor::idle_drop_paths (vector<string> paths, framepos_t frame, double ypos, bool copy)
+Editor::idle_drop_paths (vector<string> paths, samplepos_t sample, double ypos, bool copy)
 {
-       drop_paths_part_two (paths, frame, ypos, copy);
+       drop_paths_part_two (paths, sample, ypos, copy);
        return false;
 }
 
 void
-Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, double ypos, bool copy)
+Editor::drop_paths_part_two (const vector<string>& paths, samplepos_t sample, double ypos, bool copy)
 {
        RouteTimeAxisView* tv;
-       
+
        /* MIDI files must always be imported, because we consider them
         * writable. So split paths into two vectors, and follow the import
         * path on the MIDI part.
@@ -424,14 +417,15 @@ Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, doub
 
                /* drop onto canvas background: create new tracks */
 
-               frame = 0;
+               sample = 0;
                InstrumentSelector is; // instantiation builds instrument-list and sets default.
-               do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, frame, is.selected_instrument());
-               
-               if (Profile->get_sae() || UIConfiguration::instance().get_only_copy_imported_files() || copy) {
-                       do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack, SrcBest, frame);
+               do_import (midi_paths, Editing::ImportDistinctFiles, ImportAsTrack, SrcBest, SMFTrackName, SMFTempoIgnore, sample, is.selected_instrument());
+
+               if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
+                       do_import (audio_paths, Editing::ImportDistinctFiles, Editing::ImportAsTrack,
+                                  SrcBest, SMFTrackName, SMFTempoIgnore, sample);
                } else {
-                       do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, frame);
+                       do_embed (audio_paths, Editing::ImportDistinctFiles, ImportAsTrack, sample);
                }
 
        } else if ((tv = dynamic_cast<RouteTimeAxisView*> (tvp.first)) != 0) {
@@ -442,12 +436,14 @@ Editor::drop_paths_part_two (const vector<string>& paths, framepos_t frame, doub
                        /* select the track, then embed/import */
                        selection->set (tv);
 
-                       do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack, SrcBest, frame);
+                       do_import (midi_paths, Editing::ImportSerializeFiles, ImportToTrack,
+                                  SrcBest, SMFTrackName, SMFTempoIgnore, sample);
 
-                       if (Profile->get_sae() || UIConfiguration::instance().get_only_copy_imported_files() || copy) {
-                               do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack, SrcBest, frame);
+                       if (UIConfiguration::instance().get_only_copy_imported_files() || copy) {
+                               do_import (audio_paths, Editing::ImportSerializeFiles, Editing::ImportToTrack,
+                                          SrcBest, SMFTrackName, SMFTempoIgnore, sample);
                        } else {
-                               do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, frame);
+                               do_embed (audio_paths, Editing::ImportSerializeFiles, ImportToTrack, sample);
                        }
                }
        }
@@ -461,7 +457,6 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
 {
        vector<string> paths;
        GdkEvent ev;
-       framepos_t frame;
        double cy;
 
        if (convert_drop_to_paths (paths, context, x, y, data, info, time) == 0) {
@@ -473,19 +468,18 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
                ev.button.x = x;
                ev.button.y = y;
 
-               frame = window_event_sample (&ev, 0, &cy);
-
-               snap_to (frame);
+               MusicSample when (window_event_sample (&ev, 0, &cy), 0);
+               snap_to (when);
 
                bool copy = ((context->get_actions() & (Gdk::ACTION_COPY | Gdk::ACTION_LINK | Gdk::ACTION_MOVE)) == Gdk::ACTION_COPY);
-#ifdef GTKOSX
+#ifdef __APPLE__
                /* We are not allowed to call recursive main event loops from within
                   the main event loop with GTK/Quartz. Since import/embed wants
                   to push up a progress dialog, defer all this till we go idle.
                */
-               Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, frame, cy, copy));
+               Glib::signal_idle().connect (sigc::bind (sigc::mem_fun (*this, &Editor::idle_drop_paths), paths, when.sample, cy, copy));
 #else
-               drop_paths_part_two (paths, frame, cy, copy);
+               drop_paths_part_two (paths, when.sample, cy, copy);
 #endif
        }
 
@@ -500,6 +494,12 @@ Editor::drop_paths (const RefPtr<Gdk::DragContext>& context,
 void
 Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
 {
+       Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
+
+       if (!toplevel) {
+               return;
+       }
+
        if (!UIConfiguration::instance().get_autoscroll_editor () || autoscroll_active ()) {
                return;
        }
@@ -520,13 +520,23 @@ Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
 
        if (from_headers) {
                alloc = controls_layout.get_allocation ();
-       } else {        
+
+               int wx, wy;
+
+               controls_layout.get_parent()->translate_coordinates (*toplevel,
+                                                                    alloc.get_x(), alloc.get_y(),
+                                                                    wx, wy);
+
+               scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
+
+
+       } else {
                alloc = _track_canvas_viewport->get_allocation ();
 
                /* reduce height by the height of the timebars, which happens
                   to correspond to the position of the hv_scroll_group.
                */
-               
+
                alloc.set_height (alloc.get_height() - hv_scroll_group->position().y);
                alloc.set_y (alloc.get_y() + hv_scroll_group->position().y);
 
@@ -539,27 +549,32 @@ Editor::maybe_autoscroll (bool allow_horiz, bool allow_vert, bool from_headers)
 
                /* the effective width of the autoscroll boundary so
                   that we start scrolling before we hit the edge.
-                  
+
                   this helps when the window is slammed up against the
                   right edge of the screen, making it hard to scroll
                   effectively.
                */
-               
-               if (alloc.get_width() > 20) { 
+
+               if (alloc.get_width() > 20) {
                        alloc.set_width (alloc.get_width() - 20);
                        alloc.set_x (alloc.get_x() + 10);
-               } 
+               }
+
+               int wx, wy;
 
+               _track_canvas_viewport->get_parent()->translate_coordinates (*toplevel,
+                                                                            alloc.get_x(), alloc.get_y(),
+                                                                            wx, wy);
+
+               scrolling_boundary = ArdourCanvas::Rect (wx, wy, wx + alloc.get_width(), wy + alloc.get_height());
        }
-       
-       scrolling_boundary = ArdourCanvas::Rect (alloc.get_x(), alloc.get_y(), alloc.get_x() + alloc.get_width(), alloc.get_y() + alloc.get_height());
-       
+
        int x, y;
        Gdk::ModifierType mask;
 
-       get_window()->get_pointer (x, y, mask);
+       toplevel->get_window()->get_pointer (x, y, mask);
 
-       if ((allow_horiz && ((x < scrolling_boundary.x0 && leftmost_frame > 0) || x >= scrolling_boundary.x1)) ||
+       if ((allow_horiz && ((x < scrolling_boundary.x0 && _leftmost_sample > 0) || x >= scrolling_boundary.x1)) ||
            (allow_vert && ((y < scrolling_boundary.y0 && vertical_adjustment.get_value() > 0)|| y >= scrolling_boundary.y1))) {
                start_canvas_autoscroll (allow_horiz, allow_vert, scrolling_boundary);
        }
@@ -571,22 +586,81 @@ Editor::autoscroll_active () const
        return autoscroll_connection.connected ();
 }
 
+std::pair <samplepos_t,samplepos_t>
+Editor::session_gui_extents (bool use_extra) const
+{
+       if (!_session) {
+               return std::pair <samplepos_t,samplepos_t>(max_samplepos,0);
+       }
+
+       samplecnt_t session_extent_start = _session->current_start_sample();
+       samplecnt_t session_extent_end = _session->current_end_sample();
+
+       /* calculate the extents of all regions in every playlist
+        * NOTE: we should listen to playlists, and cache these values so we don't calculate them every time.
+        */
+       {
+               boost::shared_ptr<RouteList> rl = _session->get_routes();
+               for (RouteList::iterator r = rl->begin(); r != rl->end(); ++r) {
+                       boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> (*r);
+                       if (tr) {
+                               boost::shared_ptr<Playlist> pl = tr->playlist();
+                               if (pl && !pl->all_regions_empty()) {
+                                       pair<samplepos_t, samplepos_t> e;
+                                       e = pl->get_extent();
+                                       if (e.first < session_extent_start) {
+                                               session_extent_start = e.first;
+                                       }
+                                       if (e.second > session_extent_end) {
+                                               session_extent_end = e.second;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       /* ToDo: also incorporate automation regions (in case the session has no audio/midi but is just used for automating plugins or the like) */
+
+       /* add additional time to the ui extents (user-defined in config) */
+       if (use_extra) {
+               samplecnt_t const extra = UIConfiguration::instance().get_extra_ui_extents_time() * 60 * _session->nominal_sample_rate();
+               session_extent_end += extra;
+               session_extent_start -= extra;
+       }
+
+       /* range-check */
+       if (session_extent_end > max_samplepos) {
+               session_extent_end = max_samplepos;
+       }
+       if (session_extent_start < 0) {
+               session_extent_start = 0;
+       }
+
+       std::pair <samplepos_t,samplepos_t> ret (session_extent_start, session_extent_end);
+       return ret;
+}
+
 bool
 Editor::autoscroll_canvas ()
 {
        int x, y;
        Gdk::ModifierType mask;
-       frameoffset_t dx = 0;
+       sampleoffset_t dx = 0;
        bool no_stop = false;
+       Gtk::Window* toplevel = dynamic_cast<Gtk::Window*>(contents().get_toplevel());
+
+       if (!toplevel) {
+               return false;
+       }
 
-       get_window()->get_pointer (x, y, mask);
+       toplevel->get_window()->get_pointer (x, y, mask);
 
        VisualChange vc;
        bool vertical_motion = false;
 
        if (autoscroll_horizontal_allowed) {
 
-               framepos_t new_frame = leftmost_frame;
+               samplepos_t new_sample = _leftmost_sample;
 
                /* horizontal */
 
@@ -598,43 +672,47 @@ Editor::autoscroll_canvas ()
 
                        dx = pixel_to_sample (dx);
 
-                       if (leftmost_frame < max_framepos - dx) {
-                               new_frame = leftmost_frame + dx;
+                       dx *= UIConfiguration::instance().get_draggable_playhead_speed();
+
+                       if (_leftmost_sample < max_samplepos - dx) {
+                               new_sample = _leftmost_sample + dx;
                        } else {
-                               new_frame = max_framepos;
+                               new_sample = max_samplepos;
                        }
 
                        no_stop = true;
 
                } else if (x < autoscroll_boundary.x0) {
-                       
+
                        dx = autoscroll_boundary.x0 - x;
                        dx += 10 + (2 * (autoscroll_cnt/2));
 
                        dx = pixel_to_sample (dx);
 
-                       if (leftmost_frame >= dx) {
-                               new_frame = leftmost_frame - dx;
+                       dx *= UIConfiguration::instance().get_draggable_playhead_speed();
+
+                       if (_leftmost_sample >= dx) {
+                               new_sample = _leftmost_sample - dx;
                        } else {
-                               new_frame = 0;
+                               new_sample = 0;
                        }
 
                        no_stop = true;
                }
-               
-               if (new_frame != leftmost_frame) {
-                       vc.time_origin = new_frame;
+
+               if (new_sample != _leftmost_sample) {
+                       vc.time_origin = new_sample;
                        vc.add (VisualChange::TimeOrigin);
                }
        }
 
        if (autoscroll_vertical_allowed) {
-               
+
                // const double vertical_pos = vertical_adjustment.get_value();
                const int speed_factor = 10;
 
-               /* vertical */ 
-               
+               /* vertical */
+
                if (y < autoscroll_boundary.y0) {
 
                        /* scroll to make higher tracks visible */
@@ -643,6 +721,7 @@ Editor::autoscroll_canvas ()
                                scroll_up_one_track ();
                                vertical_motion = true;
                        }
+                       no_stop = true;
 
                } else if (y > autoscroll_boundary.y1) {
 
@@ -650,9 +729,9 @@ Editor::autoscroll_canvas ()
                                scroll_down_one_track ();
                                vertical_motion = true;
                        }
+                       no_stop = true;
                }
 
-               no_stop = true;
        }
 
        if (vc.pending || vertical_motion) {
@@ -671,18 +750,18 @@ Editor::autoscroll_canvas ()
 
                ev.type = GDK_MOTION_NOTIFY;
                ev.state = Gdk::BUTTON1_MASK;
-               
+
                /* the motion handler expects events in canvas coordinate space */
 
                /* we asked for the mouse position above (::get_pointer()) via
-                * our own top level window (we being the Editor). Convert into 
+                * our own top level window (we being the Editor). Convert into
                 * coordinates within the canvas window.
                 */
 
                int cx;
                int cy;
 
-               translate_coordinates (*_track_canvas, x, y, cx, cy);
+               toplevel->translate_coordinates (*_track_canvas, x, y, cx, cy);
 
                /* clamp x and y to remain within the autoscroll boundary,
                 * which is defined in window coordinates
@@ -698,20 +777,21 @@ Editor::autoscroll_canvas ()
                ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
                ev.x = d.x;
                ev.y = d.y;
+               ev.state = mask;
 
                motion_handler (0, (GdkEvent*) &ev, true);
-               
+
        } else if (no_stop) {
 
                /* not changing visual state but pointer is outside the scrolling boundary
-                * so we still need to deliver a fake motion event 
+                * so we still need to deliver a fake motion event
                 */
 
                GdkEventMotion ev;
 
                ev.type = GDK_MOTION_NOTIFY;
                ev.state = Gdk::BUTTON1_MASK;
-               
+
                /* the motion handler expects events in canvas coordinate space */
 
                /* first convert from Editor window coordinates to canvas
@@ -733,14 +813,15 @@ Editor::autoscroll_canvas ()
                }
                y = min (max ((ArdourCanvas::Coord) y, autoscroll_boundary.y0), autoscroll_boundary.y1);
 
-               translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
+               toplevel->translate_coordinates (*_track_canvas_viewport, x, y, cx, cy);
 
                ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (cx, cy));
                ev.x = d.x;
                ev.y = d.y;
+               ev.state = mask;
 
                motion_handler (0, (GdkEvent*) &ev, true);
-               
+
        } else {
                stop_canvas_autoscroll ();
                return false;
@@ -749,7 +830,7 @@ Editor::autoscroll_canvas ()
        autoscroll_cnt++;
 
        return true; /* call me again */
-}      
+}
 
 void
 Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const ArdourCanvas::Rect& boundary)
@@ -760,7 +841,6 @@ Editor::start_canvas_autoscroll (bool allow_horiz, bool allow_vert, const Ardour
 
        stop_canvas_autoscroll ();
 
-       autoscroll_cnt = 0;
        autoscroll_horizontal_allowed = allow_horiz;
        autoscroll_vertical_allowed = allow_vert;
        autoscroll_boundary = boundary;
@@ -779,6 +859,7 @@ void
 Editor::stop_canvas_autoscroll ()
 {
        autoscroll_connection.disconnect ();
+       autoscroll_cnt = 0;
 }
 
 Editor::EnterContext*
@@ -793,22 +874,46 @@ Editor::get_enter_context(ItemType type)
 }
 
 bool
-Editor::left_track_canvas (GdkEventCrossing */*ev*/)
+Editor::left_track_canvas (GdkEventCrossing* ev)
 {
+       const bool was_within = within_track_canvas;
        DropDownKeys ();
        within_track_canvas = false;
        set_entered_track (0);
        set_entered_regionview (0);
        reset_canvas_action_sensitivity (false);
+
+       if (was_within) {
+               if (ev->detail == GDK_NOTIFY_NONLINEAR ||
+                   ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
+                       /* context menu or something similar */
+                       sensitize_the_right_region_actions (false);
+               } else {
+                       sensitize_the_right_region_actions (true);
+               }
+       }
+
        return false;
 }
 
 bool
-Editor::entered_track_canvas (GdkEventCrossing */*ev*/)
+Editor::entered_track_canvas (GdkEventCrossing* ev)
 {
+       const bool was_within = within_track_canvas;
        within_track_canvas = true;
        reset_canvas_action_sensitivity (true);
-       return FALSE;
+
+       if (!was_within) {
+               if (ev->detail == GDK_NOTIFY_NONLINEAR ||
+                   ev->detail == GDK_NOTIFY_NONLINEAR_VIRTUAL) {
+                       /* context menu or something similar */
+                       sensitize_the_right_region_actions (false);
+               } else {
+                       sensitize_the_right_region_actions (true);
+               }
+       }
+
+       return false;
 }
 
 void
@@ -828,7 +933,7 @@ Editor::ensure_time_axis_view_is_visible (TimeAxisView const & track, bool at_to
        double const track_min_y = track.y_position ();
        double const track_max_y = track.y_position () + track.effective_height ();
 
-       if (!at_top && 
+       if (!at_top &&
            (track_min_y >= current_view_min_y &&
             track_max_y < current_view_max_y)) {
                /* already visible, and caller did not ask to place it at the
@@ -870,23 +975,14 @@ Editor::set_horizontal_position (double p)
 {
        horizontal_adjustment.set_value (p);
 
-       leftmost_frame = (framepos_t) floor (p * samples_per_pixel);
-
-       update_fixed_rulers ();
-       redisplay_tempo (true);
-
-       if (pending_visual_change.idle_handler_id < 0) {
-               _summary->set_overlays_dirty ();
-       }
-
-       update_video_timeline();
+       _leftmost_sample = (samplepos_t) floor (p * samples_per_pixel);
 }
 
 void
 Editor::color_handler()
 {
-       ArdourCanvas::Color base = UIConfiguration::instance().color ("ruler base");
-       ArdourCanvas::Color text = UIConfiguration::instance().color ("ruler text");
+       Gtkmm2ext::Color base = UIConfiguration::instance().color ("ruler base");
+       Gtkmm2ext::Color text = UIConfiguration::instance().color ("ruler text");
        timecode_ruler->set_fill_color (base);
        timecode_ruler->set_outline_color (text);
        minsec_ruler->set_fill_color (base);
@@ -895,7 +991,7 @@ Editor::color_handler()
        samples_ruler->set_outline_color (text);
        bbt_ruler->set_fill_color (base);
        bbt_ruler->set_outline_color (text);
-       
+
        playhead_cursor->set_color (UIConfiguration::instance().color ("play head"));
 
        meter_bar->set_fill_color (UIConfiguration::instance().color_mod ("meter bar", "marker bar"));
@@ -945,12 +1041,14 @@ Editor::color_handler()
 
        refresh_location_display ();
 
+       NoteBase::set_colors ();
+
        /* redraw the whole thing */
        _track_canvas->set_background_color (UIConfiguration::instance().color ("arrange base"));
        _track_canvas->queue_draw ();
-        
+
 /*
-       redisplay_tempo (true);
+       redisplay_grid (true);
 
        if (_session)
              _session->tempo_map().apply_with_metrics (*this, &Editor::draw_metric_marks); // redraw metric markers
@@ -960,7 +1058,7 @@ Editor::color_handler()
 double
 Editor::horizontal_position () const
 {
-       return sample_to_pixel (leftmost_frame);
+       return sample_to_pixel (_leftmost_sample);
 }
 
 bool
@@ -1054,26 +1152,6 @@ Editor::pop_canvas_cursor ()
        }
 }
 
-Gdk::Cursor*
-Editor::which_grabber_cursor () const
-{
-       Gdk::Cursor* c = _cursors->grabber;
-
-       switch (_edit_point) {
-       case EditAtMouse:
-               c = _cursors->grabber_edit_point;
-               break;
-       default:
-               boost::shared_ptr<Movable> m = _movable.lock();
-               if (m && m->locked()) {
-                       c = _cursors->speaker;
-               }
-               break;
-       }
-
-       return c;
-}
-
 Gdk::Cursor*
 Editor::which_trim_cursor (bool left) const
 {
@@ -1082,9 +1160,9 @@ Editor::which_trim_cursor (bool left) const
        }
 
        Trimmable::CanTrim ct = entered_regionview->region()->can_trim ();
-               
+
        if (left) {
-               
+
                if (ct & Trimmable::FrontTrimEarlier) {
                        return _cursors->left_side_trim;
                } else {
@@ -1112,7 +1190,7 @@ Editor::which_mode_cursor () const
        case MouseCut:
                mode_cursor = _cursors->scissors;
                break;
-                       
+
        case MouseObject:
        case MouseContent:
                /* don't use mode cursor, pick a grabber cursor based on the item */
@@ -1138,20 +1216,20 @@ Editor::which_mode_cursor () const
                get_pointer_position (x, y);
 
                if (x >= 0 && y >= 0) {
-                       
+
                        vector<ArdourCanvas::Item const *> items;
 
                        /* Note how we choose a specific scroll group to get
                         * items from. This could be problematic.
                         */
-                       
+
                        hv_scroll_group->add_items_at_point (ArdourCanvas::Duple (x,y), items);
-                       
-                       // first item will be the upper most 
-                       
+
+                       // first item will be the upper most
+
                        if (!items.empty()) {
                                const ArdourCanvas::Item* i = items.front();
-                               
+
                                if (i && i->parent() && i->parent()->get_data (X_("timeselection"))) {
                                        pair<TimeAxisView*, int> tvp = trackview_by_y_position (_last_motion_y);
                                        if (dynamic_cast<AutomationTimeAxisView*> (tvp.first)) {
@@ -1173,7 +1251,7 @@ Editor::which_track_cursor () const
        switch (_join_object_range_state) {
        case JOIN_OBJECT_RANGE_NONE:
        case JOIN_OBJECT_RANGE_OBJECT:
-               cursor = which_grabber_cursor ();
+               cursor = _cursors->grabber;
                break;
        case JOIN_OBJECT_RANGE_RANGE:
                cursor = _cursors->selector;
@@ -1200,7 +1278,7 @@ Editor::which_canvas_cursor(ItemType type) const
                        break;
                }
        }
-       
+
        if ((mouse_mode == MouseObject || get_smart_mode ()) ||
            mouse_mode == MouseContent) {
 
@@ -1220,14 +1298,7 @@ Editor::which_canvas_cursor(ItemType type) const
                        cursor = which_track_cursor ();
                        break;
                case PlayheadCursorItem:
-                       switch (_edit_point) {
-                       case EditAtMouse:
-                               cursor = _cursors->grabber_edit_point;
-                               break;
-                       default:
-                               cursor = _cursors->grabber;
-                               break;
-                       }
+                       cursor = _cursors->grabber;
                        break;
                case SelectionItem:
                        cursor = _cursors->selector;
@@ -1269,13 +1340,13 @@ Editor::which_canvas_cursor(ItemType type) const
                        cursor = _cursors->cross_hair;
                        break;
                case LeftFrameHandle:
-                       if ( effective_mouse_mode() == MouseObject )  // (smart mode): if the user is in the btm half, show the trim cursor
+                       if (effective_mouse_mode() == MouseObject) // (smart mode): if the user is in the btm half, show the trim cursor
                                cursor = which_trim_cursor (true);
                        else
-                               cursor = _cursors->selector;  // (smart mode): in the top half, just show the selection (range) cursor
+                               cursor = _cursors->selector; // (smart mode): in the top half, just show the selection (range) cursor
                        break;
                case RightFrameHandle:
-                       if ( effective_mouse_mode() == MouseObject )  //see above
+                       if (effective_mouse_mode() == MouseObject) // see above
                                cursor = which_trim_cursor (false);
                        else
                                cursor = _cursors->selector;
@@ -1296,7 +1367,7 @@ Editor::which_canvas_cursor(ItemType type) const
                }
 
        } else if (mouse_mode == MouseDraw) {
-               
+
                /* ControlPointItem is not really specific to region gain mode
                   but it is the same cursor so don't worry about this for now.
                   The result is that we'll see the fader cursor if we enter
@@ -1337,7 +1408,7 @@ Editor::which_canvas_cursor(ItemType type) const
        case VideoBarItem:
        case TransportMarkerBarItem:
        case DropZoneItem:
-               cursor = which_grabber_cursor();
+               cursor = _cursors->grabber;
                break;
 
        default: