replace Lock^Wshopping-bag icon with button-sensitivity
[ardour.git] / gtk2_ardour / editor_summary.cc
index 4efb57ffc44e279320f97fc7d49a876f6e95c71a..0dc728d18e26e837e6b3e15ad87c63d63be5c85e 100644 (file)
@@ -18,6 +18,7 @@
 */
 
 #include "ardour/session.h"
+
 #include "time_axis_view.h"
 #include "streamview.h"
 #include "editor_summary.h"
@@ -54,9 +55,11 @@ EditorSummary::EditorSummary (Editor* e)
          _old_follow_playhead (false)
 {
        Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
-       _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), ui_bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
+       Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
+       _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
 
-       add_events (Gdk::POINTER_MOTION_MASK);  
+       add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
+       set_flags (get_flags() | Gtk::CAN_FOCUS);
 }
 
 /** Connect to a session.
@@ -75,60 +78,9 @@ EditorSummary::set_session (Session* s)
         */
 
        if (_session) {
-               _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_dirty, this), gui_context());
-               _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_dirty, this), gui_context());
-       }
-}
-
-/** Handle an expose event.
- *  @param event Event from GTK.
- */
-bool
-EditorSummary::on_expose_event (GdkEventExpose* event)
-{
-       CairoWidget::on_expose_event (event);
-
-       if (_session == 0) {
-               return false;
-       }
-
-       cairo_t* cr = gdk_cairo_create (get_window()->gobj());
-
-       /* Render the view rectangle.  If there is an editor visual pending, don't update
-          the view rectangle now --- wait until the expose event that we'll get after
-          the visual change.  This prevents a flicker.
-       */
-
-       if (_editor->pending_visual_change.idle_handler_id < 0) {
-               get_editor (&_view_rectangle_x, &_view_rectangle_y);
+               _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
+               _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context());
        }
-
-       cairo_move_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
-       cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.first);
-       cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.second);
-       cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.second);
-       cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
-       cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
-       cairo_fill_preserve (cr);
-       cairo_set_line_width (cr, 1);
-       cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
-       cairo_stroke (cr);
-
-       /* Playhead */
-
-       cairo_set_line_width (cr, 1);
-       /* XXX: colour should be set from configuration file */
-       cairo_set_source_rgba (cr, 1, 0, 0, 1);
-
-       double const p = (_editor->playhead_cursor->current_frame - _start) * _x_scale;
-       cairo_move_to (cr, p, 0);
-       cairo_line_to (cr, p, _height);
-       cairo_stroke (cr);
-       _last_playhead = p;
-
-       cairo_destroy (cr);
-
-       return true;
 }
 
 /** Render the required regions to a cairo context.
@@ -137,10 +89,10 @@ EditorSummary::on_expose_event (GdkEventExpose* event)
 void
 EditorSummary::render (cairo_t* cr)
 {
-       /* background */
+       /* background (really just the dividing lines between tracks */
 
        cairo_set_source_rgb (cr, 0, 0, 0);
-       cairo_rectangle (cr, 0, 0, _width, _height);
+       cairo_rectangle (cr, 0, 0, get_width(), get_height());
        cairo_fill (cr);
 
        if (_session == 0) {
@@ -148,7 +100,7 @@ EditorSummary::render (cairo_t* cr)
        }
 
        /* compute start and end points for the summary */
-       
+
        framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
        double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
        _start = theoretical_start > 0 ? theoretical_start : 0;
@@ -161,16 +113,16 @@ EditorSummary::render (cairo_t* cr)
                        ++N;
                }
        }
-       
+
        if (N == 0) {
                _track_height = 16;
        } else {
-               _track_height = (double) _height / N;
+               _track_height = (double) get_height() / N;
        }
 
        /* calculate x scale */
        if (_end != _start) {
-               _x_scale = static_cast<double> (_width) / (_end - _start);
+               _x_scale = static_cast<double> (get_width()) / (_end - _start);
        } else {
                _x_scale = 1;
        }
@@ -184,12 +136,14 @@ EditorSummary::render (cairo_t* cr)
                        continue;
                }
 
+               /* paint a non-bg colored strip to represent the track itself */
+
                cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
-               cairo_set_line_width (cr, _track_height - 2);
+               cairo_set_line_width (cr, _track_height - 1);
                cairo_move_to (cr, 0, y + _track_height / 2);
-               cairo_line_to (cr, _width, y + _track_height / 2);
+               cairo_line_to (cr, get_width(), y + _track_height / 2);
                cairo_stroke (cr);
-               
+
                StreamView* s = (*i)->view ();
 
                if (s) {
@@ -201,7 +155,7 @@ EditorSummary::render (cairo_t* cr)
                                                       y + _track_height / 2
                                                       ));
                }
-               
+
                y += _track_height;
        }
 
@@ -210,15 +164,48 @@ EditorSummary::render (cairo_t* cr)
        cairo_set_line_width (cr, 1);
        cairo_set_source_rgb (cr, 1, 1, 0);
 
-       double const p = (_session->current_start_frame() - _start) * _x_scale;
+       const double p = (_session->current_start_frame() - _start) * _x_scale;
        cairo_move_to (cr, p, 0);
-       cairo_line_to (cr, p, _height);
+       cairo_line_to (cr, p, get_height());
        cairo_stroke (cr);
 
        double const q = (_session->current_end_frame() - _start) * _x_scale;
        cairo_move_to (cr, q, 0);
-       cairo_line_to (cr, q, _height);
+       cairo_line_to (cr, q, get_height());
+       cairo_stroke (cr);
+
+       /* Render the view rectangle.  If there is an editor visual pending, don't update
+          the view rectangle now --- wait until the expose event that we'll get after
+          the visual change.  This prevents a flicker.
+       */
+
+       if (_editor->pending_visual_change.idle_handler_id < 0) {
+               get_editor (&_view_rectangle_x, &_view_rectangle_y);
+       }
+
+       cairo_move_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
+       cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.first);
+       cairo_line_to (cr, _view_rectangle_x.second, _view_rectangle_y.second);
+       cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.second);
+       cairo_line_to (cr, _view_rectangle_x.first, _view_rectangle_y.first);
+       cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
+       cairo_fill_preserve (cr);
+       cairo_set_line_width (cr, 1);
+       cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+       cairo_stroke (cr);
+
+       /* Playhead */
+
+       cairo_set_line_width (cr, 1);
+       /* XXX: colour should be set from configuration file */
+       cairo_set_source_rgba (cr, 1, 0, 0, 1);
+
+       const double ph= playhead_frame_to_position (_editor->playhead_cursor->current_frame);
+       cairo_move_to (cr, ph, 0);
+       cairo_line_to (cr, ph, get_height());
        cairo_stroke (cr);
+       _last_playhead = ph;
+
 }
 
 /** Render a region for the summary.
@@ -251,10 +238,19 @@ EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
 void
 EditorSummary::set_overlays_dirty ()
 {
-       ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty)
+       ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
        queue_draw ();
 }
 
+/** Set the summary so that just the overlays (viewbox, playhead etc.) in a given area will be re-rendered */
+void
+EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
+{
+       ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
+       queue_draw_area (x, y, w, h);
+}
+
+
 /** Handle a size request.
  *  @param req GTK requisition
  */
@@ -278,27 +274,78 @@ EditorSummary::centre_on_click (GdkEventButton* ev)
        double ex = ev->x - w / 2;
        if (ex < 0) {
                ex = 0;
-       } else if ((ex + w) > _width) {
-               ex = _width - w;
+       } else if ((ex + w) > get_width()) {
+               ex = get_width() - w;
        }
 
        double const h = yr.second - yr.first;
        double ey = ev->y - h / 2;
        if (ey < 0) {
                ey = 0;
-       } else if ((ey + h) > _height) {
-               ey = _height - h;
+       } else if ((ey + h) > get_height()) {
+               ey = get_height() - h;
        }
 
        set_editor (ex, ey);
 }
 
+bool 
+EditorSummary::on_enter_notify_event (GdkEventCrossing*)
+{
+       grab_focus ();
+       Keyboard::magic_widget_grab_focus ();
+       return false;
+}
+
+bool 
+EditorSummary::on_leave_notify_event (GdkEventCrossing*)
+{
+       /* there are no inferior/child windows, so any leave event means that
+          we're gone.
+       */
+       Keyboard::magic_widget_drop_focus ();
+       return false;
+}
+
+bool
+EditorSummary::on_key_press_event (GdkEventKey* key)
+{
+       gint x, y;
+       GtkAccelKey set_playhead_accel;
+       if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
+               if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
+                       if (_session) {
+                               get_pointer (x, y);
+                               _session->request_locate (_start + (framepos_t) x / _x_scale, _session->transport_rolling());
+                               return true;
+                       }
+               }
+       }
+
+       return false;
+}
+
+bool
+EditorSummary::on_key_release_event (GdkEventKey* key)
+{
+
+       GtkAccelKey set_playhead_accel;
+       if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
+               if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
+                       return true;
+               }
+       }
+       return false;
+}
+
 /** Handle a button press.
  *  @param ev GTK event.
  */
 bool
 EditorSummary::on_button_press_event (GdkEventButton* ev)
 {
+       _old_follow_playhead = _editor->follow_playhead ();
+       
        if (ev->button == 1) {
 
                pair<double, double> xr;
@@ -320,9 +367,13 @@ EditorSummary::on_button_press_event (GdkEventButton* ev)
                        _zoom_position = get_position (ev->x, ev->y);
                        _zoom_dragging = true;
                        _editor->_dragging_playhead = true;
-                       _old_follow_playhead = _editor->follow_playhead ();
                        _editor->set_follow_playhead (false);
 
+                       if (suspending_editor_updates ()) {
+                               get_editor (&_pending_editor_x, &_pending_editor_y);
+                               _pending_editor_changed = false;
+                       }
+                       
                } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
 
                        /* secondary-modifier-click: locate playhead */
@@ -338,10 +389,13 @@ EditorSummary::on_button_press_event (GdkEventButton* ev)
 
                        /* start a move drag */
 
+                       /* get the editor's state in case we are suspending updates */
+                       get_editor (&_pending_editor_x, &_pending_editor_y);
+                       _pending_editor_changed = false;
+
                        _move_dragging = true;
                        _moved = false;
                        _editor->_dragging_playhead = true;
-                       _old_follow_playhead = _editor->follow_playhead ();
                        _editor->set_follow_playhead (false);
                }
        }
@@ -349,18 +403,41 @@ EditorSummary::on_button_press_event (GdkEventButton* ev)
        return true;
 }
 
+/** @return true if we are currently suspending updates to the editor's viewport,
+ *  which we do if configured to do so, and if in a drag of some kind.
+ */
+bool
+EditorSummary::suspending_editor_updates () const
+{
+       return (!Config->get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
+}
+
 /** Fill in x and y with the editor's current viewable area in summary coordinates */
 void
 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
 {
        assert (x);
        assert (y);
-       
-       x->first = (_editor->leftmost_position () - _start) * _x_scale;
-       x->second = x->first + _editor->current_page_frames() * _x_scale;
 
-       y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
-       y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->canvas_height() - _editor->get_canvas_timebars_vsize());
+       if (suspending_editor_updates ()) {
+
+               /* We are dragging, and configured not to update the editor window during drags,
+                  so just return where the editor will be when the drag finishes.
+               */
+                  
+               *x = _pending_editor_x;
+               *y = _pending_editor_y;
+
+       } else {
+
+               /* Otherwise query the editor for its actual position */
+
+               x->first = (_editor->leftmost_position () - _start) * _x_scale;
+               x->second = x->first + _editor->current_page_frames() * _x_scale;
+               
+               y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
+               y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->canvas_height() - _editor->get_canvas_timebars_vsize());
+       }
 }
 
 /** Get an expression of the position of a point with respect to the view rectangle */
@@ -377,7 +454,7 @@ EditorSummary::get_position (double x, double y) const
        int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
        y_edge_size = min (y_edge_size, 8);
        y_edge_size = max (y_edge_size, 1);
-       
+
        bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
        bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
        bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
@@ -486,7 +563,7 @@ EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
                }
 
                set_editor (x, y);
-               set_cursor (_start_position);
+               // set_cursor (_start_position);
 
        } else if (_zoom_dragging) {
 
@@ -521,11 +598,17 @@ EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
 bool
 EditorSummary::on_button_release_event (GdkEventButton*)
 {
+       bool const was_suspended = suspending_editor_updates ();
+       
        _move_dragging = false;
        _zoom_dragging = false;
        _editor->_dragging_playhead = false;
        _editor->set_follow_playhead (_old_follow_playhead, false);
-       
+
+       if (was_suspended && _pending_editor_changed) {
+               set_editor (_pending_editor_x, _pending_editor_y);
+       }
+               
        return true;
 }
 
@@ -540,35 +623,55 @@ EditorSummary::on_scroll_event (GdkEventScroll* ev)
        double x = xr.first;
        double y = yr.first;
 
-       double amount = 8;
-
-       if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
-               amount = 64;
-       } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
-               amount = 1;
-       }
-
-       if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
-
-               /* primary-wheel == left-right scrolling */
-
-               if (ev->direction == GDK_SCROLL_UP) {
-                       x += amount;
-               } else if (ev->direction == GDK_SCROLL_DOWN) {
-                       x -= amount;
-               }
-
-       } else {
-
-               if (ev->direction == GDK_SCROLL_DOWN) {
-                       y += amount;
-               } else if (ev->direction == GDK_SCROLL_UP) {
-                       y -= amount;
-               } else if (ev->direction == GDK_SCROLL_LEFT) {
-                       x -= amount;
-               } else if (ev->direction == GDK_SCROLL_RIGHT) {
-                       x += amount;
-               }
+       switch (ev->direction) {
+               case GDK_SCROLL_UP:
+                       if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
+                               x -= 64;
+                       } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
+                               _editor->temporal_zoom_step (false);
+                       } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
+                               yr.first  += 4;
+                               yr.second -= 4;
+                               set_editor (xr, yr);
+                               return true;
+                       } else {
+                               y -= 8;
+                       }
+                       break;
+               case GDK_SCROLL_DOWN:
+                       if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
+                               x += 64;
+                       } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
+                               _editor->temporal_zoom_step (true);
+                       } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
+                               yr.first  -= 4;
+                               yr.second += 4;
+                               set_editor (xr, yr);
+                               return true;
+                       } else {
+                               y += 8;
+                       }
+                       break;
+               case GDK_SCROLL_LEFT:
+                       if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
+                               x -= 64;
+                       } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
+                               x -= 1;
+                       } else {
+                               x -= 8;
+                       }
+                       break;
+               case GDK_SCROLL_RIGHT:
+                       if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
+                               x += 64;
+                       } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
+                               x += 1;
+                       } else {
+                               x += 8;
+                       }
+                       break;
+               default:
+                       break;
        }
 
        set_editor (x, y);
@@ -595,10 +698,10 @@ EditorSummary::set_editor (double const x, double const y)
                   as it also means that we won't change these variables if an idle handler
                   is merely pending but not executing.  But c'est la vie.
                */
-
+               
                return;
        }
-       
+
        set_editor_x (x);
        set_editor_y (y);
 }
@@ -608,13 +711,13 @@ EditorSummary::set_editor (double const x, double const y)
  *  x and y parameters are specified in summary coordinates.
  */
 void
-EditorSummary::set_editor (pair<double,double> const x, double const y)
+EditorSummary::set_editor (pair<double,double> const x, double const y)
 {
        if (_editor->pending_visual_change.idle_handler_id >= 0) {
                /* see comment in other set_editor () */
                return;
        }
-       
+
        set_editor_x (x);
        set_editor_y (y);
 }
@@ -624,7 +727,7 @@ EditorSummary::set_editor (pair<double,double> const & x, double const y)
  *  x and y parameters are specified in summary coordinates.
  */
 void
-EditorSummary::set_editor (pair<double,double> const & x, pair<double, double> const & y)
+EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
 {
        if (_editor->pending_visual_change.idle_handler_id >= 0) {
                /* see comment in other set_editor () */
@@ -640,9 +743,21 @@ EditorSummary::set_editor (pair<double,double> const & x, pair<double, double> c
  *  @param x new x left position in summary coordinates.
  */
 void
-EditorSummary::set_editor_x (double const x)
+EditorSummary::set_editor_x (double x)
 {
-       _editor->reset_x_origin (x / _x_scale + _start);
+       if (x < 0) {
+               x = 0;
+       }
+
+       if (suspending_editor_updates ()) {
+               double const w = _pending_editor_x.second - _pending_editor_x.first;
+               _pending_editor_x.first = x;
+               _pending_editor_x.second = x + w;
+               _pending_editor_changed = true;
+               set_dirty ();
+       } else {
+               _editor->reset_x_origin (x / _x_scale + _start);
+       }
 }
 
 /** Set the x range visible in the editor.
@@ -650,18 +765,32 @@ EditorSummary::set_editor_x (double const x)
  *  @param x new x range in summary coordinates.
  */
 void
-EditorSummary::set_editor_x (pair<double, double> const & x)
+EditorSummary::set_editor_x (pair<double, double> x)
 {
-       _editor->reset_x_origin (x.first / _x_scale + _start);
+       if (x.first < 0) {
+               x.first = 0;
+       }
 
-       double const nx = (
-               ((x.second - x.first) / _x_scale) /
-               _editor->frame_to_unit (_editor->current_page_frames())
-               );
-       
-       if (nx != _editor->get_current_zoom ()) {
-               _editor->reset_zoom (nx);
-       }       
+       if (x.second < 0) {
+               x.second = x.first + 1;
+       }
+
+       if (suspending_editor_updates ()) {
+               _pending_editor_x = x;
+               _pending_editor_changed = true;
+               set_dirty ();
+       } else {
+               _editor->reset_x_origin (x.first / _x_scale + _start);
+               
+               double const nx = (
+                       ((x.second - x.first) / _x_scale) /
+                       _editor->frame_to_unit (_editor->current_page_frames())
+                       );
+               
+               if (nx != _editor->get_current_zoom ()) {
+                       _editor->reset_zoom (nx);
+               }
+       }
 }
 
 /** Set the top of the y range visible in the editor.
@@ -674,18 +803,26 @@ EditorSummary::set_editor_y (double const y)
        double y1 = summary_y_to_editor (y);
        double const eh = _editor->canvas_height() - _editor->get_canvas_timebars_vsize ();
        double y2 = y1 + eh;
-       
+
        double const full_editor_height = _editor->full_canvas_height - _editor->get_canvas_timebars_vsize();
 
        if (y2 > full_editor_height) {
                y1 -= y2 - full_editor_height;
        }
-       
+
        if (y1 < 0) {
                y1 = 0;
        }
 
-       _editor->reset_y_origin (y1);
+       if (suspending_editor_updates ()) {
+               double const h = _pending_editor_y.second - _pending_editor_y.first;
+               _pending_editor_y.first = y;
+               _pending_editor_y.second = y + h;
+               _pending_editor_changed = true;
+               set_dirty ();
+       } else {
+               _editor->reset_y_origin (y1);
+       }
 }
 
 /** Set the y range visible in the editor.  This is achieved by scaling track heights,
@@ -694,8 +831,15 @@ EditorSummary::set_editor_y (double const y)
  *  @param y new editor range in summary coodinates.
  */
 void
-EditorSummary::set_editor_y (pair<double, double> const y)
+EditorSummary::set_editor_y (pair<double, double> const y)
 {
+       if (suspending_editor_updates ()) {
+               _pending_editor_y = y;
+               _pending_editor_changed = true;
+               set_dirty ();
+               return;
+       }
+       
        /* Compute current height of tracks between y.first and y.second.  We add up
           the total height into `total_height' and the height of complete tracks into
           `scale height'.
@@ -709,7 +853,7 @@ EditorSummary::set_editor_y (pair<double, double> const & y)
        double partial_height = 0;
        /* Height of any tracks that are fully in the desired range */
        double scale_height = 0;
-       
+
        _editor->_routes->suspend_redisplay ();
 
        for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
@@ -717,7 +861,7 @@ EditorSummary::set_editor_y (pair<double, double> const & y)
                if ((*i)->hidden()) {
                        continue;
                }
-               
+
                double const h = (*i)->effective_height ();
                total_height += h;
 
@@ -738,7 +882,7 @@ EditorSummary::set_editor_y (pair<double, double> const & y)
           enough tracks to fill it.
        */
        double const ch = min (total_height, _editor->canvas_height() - _editor->get_canvas_timebars_vsize());
-       
+
        /* hence required scale factor of the complete tracks to fit the required y range;
           the amount of space they should take up divided by the amount they currently take up.
        */
@@ -747,7 +891,7 @@ EditorSummary::set_editor_y (pair<double, double> const & y)
        yc = y;
 
        /* Scale complete tracks within the range to make it fit */
-       
+
        for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
 
                if ((*i)->hidden()) {
@@ -763,15 +907,19 @@ EditorSummary::set_editor_y (pair<double, double> const & y)
        }
 
        _editor->_routes->resume_redisplay ();
-       
+
         set_editor_y (y.first);
 }
 
 void
 EditorSummary::playhead_position_changed (framepos_t p)
 {
-       if (_session && int (p * _x_scale) != int (_last_playhead)) {
-               set_overlays_dirty ();
+       int const o = int (_last_playhead);
+       int const n = int (playhead_frame_to_position (p));
+       if (_session && o != n) {
+               int a = max(2, min (o, n));
+               int b = max (o, n);
+               set_overlays_dirty (a - 2, 0, b + 2, get_height ());
        }
 }
 
@@ -780,11 +928,11 @@ EditorSummary::summary_y_to_editor (double y) const
 {
        double ey = 0;
        for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
-               
+
                if ((*i)->hidden()) {
                        continue;
                }
-               
+
                double const h = (*i)->effective_height ();
                if (y < _track_height) {
                        /* in this track */
@@ -803,7 +951,7 @@ EditorSummary::editor_y_to_summary (double y) const
 {
        double sy = 0;
        for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
-               
+
                if ((*i)->hidden()) {
                        continue;
                }
@@ -824,9 +972,13 @@ EditorSummary::editor_y_to_summary (double y) const
 void
 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
 {
-       /* Connect to gui_changed() on the routes so that we know when their colour has changed */
        for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
-               (*i)->route()->gui_changed.connect (*this, invalidator (*this), ui_bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
+               /* Connect to gui_changed() on the route so that we know when their colour has changed */
+               (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
+               boost::shared_ptr<Track> tr = boost::dynamic_pointer_cast<Track> ((*i)->route ());
+               if (tr) {
+                       tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&CairoWidget::set_dirty, this), gui_context ());
+               }
        }
 
        set_dirty ();
@@ -839,3 +991,15 @@ EditorSummary::route_gui_changed (string c)
                set_dirty ();
        }
 }
+
+double
+EditorSummary::playhead_frame_to_position (framepos_t t) const
+{
+       return (t - _start) * _x_scale;
+}
+
+framepos_t
+EditorSummary::position_to_playhead_frame_to_position (double pos) const
+{
+       return _start  + (pos * _x_scale);
+}