replace ::cast_dynamic() with relevant ActionManager::get_*_action() calls
[ardour.git] / gtk2_ardour / editor_summary.cc
index 57cace3565188a50d6f17d0cb5895d9a5dbfaa53..e7355b577c97d57b807bbb71ad5e8eac003ff3d7 100644 (file)
@@ -21,6 +21,9 @@
 
 #include "canvas/debug.h"
 
+#include <gtkmm/menu.h>
+#include <gtkmm/menuitem.h>
+
 #include "time_axis_view.h"
 #include "streamview.h"
 #include "editor_summary.h"
@@ -33,6 +36,9 @@
 #include "editor_cursors.h"
 #include "mouse_cursors.h"
 #include "route_time_axis.h"
+#include "ui_config.h"
+
+#include "pbd/i18n.h"
 
 using namespace std;
 using namespace ARDOUR;
@@ -45,20 +51,36 @@ EditorSummary::EditorSummary (Editor* e)
        : EditorComponent (e),
          _start (0),
          _end (1),
-         _overhang_fraction (0.1),
          _x_scale (1),
          _track_height (16),
          _last_playhead (-1),
          _move_dragging (false),
-         _moved (false),
          _view_rectangle_x (0, 0),
          _view_rectangle_y (0, 0),
-         _zoom_dragging (false),
+         _zoom_trim_dragging (false),
          _old_follow_playhead (false),
+         _image (0),
          _background_dirty (true)
 {
+       CairoWidget::use_nsglview ();
        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);
+
+       UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &EditorSummary::parameter_changed));
+}
+
+EditorSummary::~EditorSummary ()
+{
+       cairo_surface_destroy (_image);
+}
+
+void
+EditorSummary::parameter_changed (string p)
+{
+
+       if (p == "color-regions-using-track-color") {
+               set_background_dirty ();
+       }
 }
 
 /** Handle a size allocation.
@@ -67,9 +89,8 @@ EditorSummary::EditorSummary (Editor* e)
 void
 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
 {
-       Gtk::EventBox::on_size_allocate (alloc);
-       _background_dirty = true;
-       set_dirty ();
+       CairoWidget::on_size_allocate (alloc);
+       set_background_dirty ();
 }
 
 
@@ -90,21 +111,26 @@ EditorSummary::set_session (Session* s)
 
        if (_session) {
                Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
-               Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
+               PresentationInfo::Change.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
                _editor->playhead_cursor->PositionChanged.connect (position_connection, invalidator (*this), boost::bind (&EditorSummary::playhead_position_changed, this, _1), gui_context());
-               _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());
+               _session->StartTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
+               _session->EndTimeChanged.connect (_session_connections, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
                _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
        }
+
+       _leftmost = max_samplepos;
+       _rightmost = 0;
 }
 
 void
 EditorSummary::render_background_image ()
 {
-       _image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, get_width (), get_height ());
+       cairo_surface_destroy (_image); // passing NULL is safe
+       _image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, get_width (), get_height ());
+
        cairo_t* cr = cairo_create (_image);
 
-       /* background (really just the dividing lines between tracks */
+       /* background (really just the dividing lines between tracks */
 
        cairo_set_source_rgb (cr, 0, 0, 0);
        cairo_rectangle (cr, 0, 0, get_width(), get_height());
@@ -112,10 +138,26 @@ EditorSummary::render_background_image ()
 
        /* 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;
+       std::pair<samplepos_t, samplepos_t> ext = _editor->session_gui_extents();
+       double theoretical_start = ext.first;
+       double theoretical_end = ext.second;
+
+       /* the summary should encompass the full extent of everywhere we've visited since the session was opened */
+       if (_leftmost < theoretical_start)
+               theoretical_start = _leftmost;
+       if (_rightmost > theoretical_end)
+               theoretical_end = _rightmost;
+
+       /* range-check */
        _start = theoretical_start > 0 ? theoretical_start : 0;
-       _end = _session->current_end_frame() + session_length * _overhang_fraction;
+       _end = theoretical_end < max_samplepos ? theoretical_end : max_samplepos;
+
+       /* calculate x scale */
+       if (_end != _start) {
+               _x_scale = static_cast<double> (get_width()) / (_end - _start);
+       } else {
+               _x_scale = 1;
+       }
 
        /* compute track height */
        int N = 0;
@@ -131,13 +173,6 @@ EditorSummary::render_background_image ()
                _track_height = (double) get_height() / N;
        }
 
-       /* calculate x scale */
-       if (_end != _start) {
-               _x_scale = static_cast<double> (get_width()) / (_end - _start);
-       } else {
-               _x_scale = 1;
-       }
-
        /* render tracks and regions */
 
        double y = 0;
@@ -149,26 +184,43 @@ EditorSummary::render_background_image ()
 
                /* 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 - 1);
-               cairo_move_to (cr, 0, y + _track_height / 2);
-               cairo_line_to (cr, get_width(), y + _track_height / 2);
-               cairo_stroke (cr);
+               if (_track_height > 4) {
+                       cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
+                       cairo_set_line_width (cr, _track_height - 1);
+                       cairo_move_to (cr, 0, y + _track_height / 2);
+                       cairo_line_to (cr, get_width(), y + _track_height / 2);
+                       cairo_stroke (cr);
+               }
 
                StreamView* s = (*i)->view ();
 
                if (s) {
-                       cairo_set_line_width (cr, _track_height * 0.6);
+                       cairo_set_line_width (cr, _track_height * 0.8);
 
                        s->foreach_regionview (sigc::bind (
-                                                      sigc::mem_fun (*this, &EditorSummary::render_region),
-                                                      cr,
-                                                      y + _track_height / 2
-                                                      ));
+                                                          sigc::mem_fun (*this, &EditorSummary::render_region),
+                                                          cr,
+                                                          y + _track_height / 2
+                                                         ));
                }
 
                y += _track_height;
        }
+
+       /* start and end markers */
+
+       cairo_set_line_width (cr, 1);
+       cairo_set_source_rgb (cr, 1, 1, 0);
+
+       const double p = (_session->current_start_sample() - _start) * _x_scale;
+       cairo_move_to (cr, p, 0);
+       cairo_line_to (cr, p, get_height());
+
+       double const q = (_session->current_end_sample() - _start) * _x_scale;
+       cairo_move_to (cr, q, 0);
+       cairo_line_to (cr, q, get_height());
+       cairo_stroke (cr);
+
        cairo_destroy (cr);
 }
 
@@ -176,54 +228,59 @@ EditorSummary::render_background_image ()
  *  @param cr Context.
  */
 void
-EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
+EditorSummary::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangle_t*)
 {
+       cairo_t* cr = ctx->cobj();
 
        if (_session == 0) {
                return;
        }
 
+       /* maintain the leftmost and rightmost locations that we've ever reached */
+       samplecnt_t const leftmost = _editor->leftmost_sample ();
+       if (leftmost < _leftmost) {
+               _leftmost = leftmost;
+               _background_dirty = true;
+       }
+       samplecnt_t const rightmost = leftmost + _editor->current_page_samples();
+       if (rightmost > _rightmost) {
+               _rightmost = rightmost;
+               _background_dirty = true;
+       }
+
+       /* draw the background (regions, markers, etc) if they've changed */
        if (!_image || _background_dirty) {
                render_background_image ();
                _background_dirty = false;
        }
 
        cairo_push_group (cr);
-       
-       cairo_rectangle (cr, 0, 0, get_width(), get_height());
-       cairo_set_source_surface (cr, _image, 0, 0);
-       cairo_paint (cr);
 
-       /* start and end markers */
+       /* Fill with the background image */
 
-       cairo_set_line_width (cr, 1);
-       cairo_set_source_rgb (cr, 1, 1, 0);
-
-       const double p = (_session->current_start_frame() - _start) * _x_scale;
-       cairo_move_to (cr, p, 0);
-       cairo_line_to (cr, p, get_height());
-
-       double const q = (_session->current_end_frame() - _start) * _x_scale;
-       cairo_move_to (cr, q, 0);
-       cairo_line_to (cr, q, get_height());
-       cairo_stroke (cr);
+       cairo_rectangle (cr, 0, 0, get_width(), get_height());
+       cairo_set_source_surface (cr, _image, 0, 0);
+       cairo_fill (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.
-       */
+        * 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);
        }
 
        int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
-       int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
-       cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height); 
-       cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
-       cairo_fill_preserve (cr);
+       std::min(8, width);
+       cairo_rectangle (cr, _view_rectangle_x.first, 0, width, get_height ());
+       cairo_set_source_rgba (cr, 1, 1, 1, 0.15);
+       cairo_fill (cr);
+
+       /* horiz zoom */
+       cairo_rectangle (cr, _view_rectangle_x.first, 0, width, get_height ());
        cairo_set_line_width (cr, 1);
-       cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
+       cairo_set_source_rgba (cr, 1, 1, 1, 0.9);
        cairo_stroke (cr);
 
        /* Playhead */
@@ -232,7 +289,7 @@ EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
        /* 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());
+       const double ph= playhead_sample_to_position (_editor->playhead_cursor->current_sample());
        cairo_move_to (cr, ph, 0);
        cairo_line_to (cr, ph, get_height());
        cairo_stroke (cr);
@@ -250,6 +307,13 @@ EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
 void
 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
 {
+       /*NOTE:  you can optimize this operation by coalescing adjacent regions into a single line stroke.
+        * In a session with a single track ~1,000 regions, this reduced render time from 14ms to 11 ms.
+        * However, you lose a lot of visual information.  The current method preserves a sense of separation between regions.
+        * The current method shows the current selection (red regions), which needs to be preserved if this is optimized.
+        * I think it's not worth it for now,  but we might choose to revisit this someday.
+        */ 
+
        uint32_t const c = r->get_fill_color ();
        cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
 
@@ -271,8 +335,10 @@ EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
 void
 EditorSummary::set_background_dirty ()
 {
-       _background_dirty = true;
-       set_dirty ();
+       if (!_background_dirty) {
+               _background_dirty = true;
+               set_dirty ();
+       }
 }
 
 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
@@ -285,9 +351,9 @@ EditorSummary::set_overlays_dirty ()
 
 /** 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)
+EditorSummary::set_overlays_dirty_rect (int x, int y, int w, int h)
 {
-       ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty);
+       ENSURE_GUI_THREAD (*this, &EditorSummary::set_overlays_dirty_rect);
        queue_draw_area (x, y, w, h);
 }
 
@@ -298,9 +364,9 @@ EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
 void
 EditorSummary::on_size_request (Gtk::Requisition *req)
 {
-       /* Use a dummy, small width and the actual height that we want */
-       req->width = 64;
-       req->height = 32;
+       /* The left/right buttons will determine our height */
+       req->width = -1;
+       req->height = -1;
 }
 
 
@@ -308,8 +374,7 @@ void
 EditorSummary::centre_on_click (GdkEventButton* ev)
 {
        pair<double, double> xr;
-       pair<double, double> yr;
-       get_editor (&xr, &yr);
+       get_editor (&xr);
 
        double const w = xr.second - xr.first;
        double ex = ev->x - w / 2;
@@ -319,18 +384,10 @@ EditorSummary::centre_on_click (GdkEventButton* ev)
                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) > get_height()) {
-               ey = get_height() - h;
-       }
-
-       set_editor (ex, ey);
+       set_editor (ex);
 }
 
-bool 
+bool
 EditorSummary::on_enter_notify_event (GdkEventCrossing*)
 {
        grab_focus ();
@@ -338,7 +395,7 @@ EditorSummary::on_enter_notify_event (GdkEventCrossing*)
        return false;
 }
 
-bool 
+bool
 EditorSummary::on_leave_notify_event (GdkEventCrossing*)
 {
        /* there are no inferior/child windows, so any leave event means that
@@ -357,7 +414,7 @@ EditorSummary::on_key_press_event (GdkEventKey* key)
                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());
+                               _session->request_locate (_start + (samplepos_t) x / _x_scale, _session->transport_rolling());
                                return true;
                        }
                }
@@ -379,6 +436,8 @@ EditorSummary::on_key_release_event (GdkEventKey* key)
        return false;
 }
 
+#include "gtkmm2ext/utils.h"
+
 /** Handle a button press.
  *  @param ev GTK event.
  */
@@ -386,61 +445,72 @@ bool
 EditorSummary::on_button_press_event (GdkEventButton* ev)
 {
        _old_follow_playhead = _editor->follow_playhead ();
-       
-       if (ev->button == 1) {
 
-               pair<double, double> xr;
-               pair<double, double> yr;
-               get_editor (&xr, &yr);
+       if (ev->button == 3) { // right-click:  show the reset menu action
+               using namespace Gtk::Menu_Helpers;
+               Gtk::Menu* m = manage (new Gtk::Menu);
+               MenuList& items = m->items ();
+               items.push_back(MenuElem(_("Reset Summary to Extents"),
+                       sigc::mem_fun(*this, &EditorSummary::reset_to_extents)));
+               m->popup (ev->button, ev->time);
+               return true;
+       }
 
-               _start_editor_x = xr;
-               _start_editor_y = yr;
-               _start_mouse_x = ev->x;
-               _start_mouse_y = ev->y;
-               _start_position = get_position (ev->x, ev->y);
+       if (ev->button != 1) {
+               return true;
+       }
 
-               if (_start_position != INSIDE && _start_position != BELOW_OR_ABOVE &&
-                   _start_position != TO_LEFT_OR_RIGHT && _start_position != OTHERWISE_OUTSIDE
-                       ) {
+       pair<double, double> xr;
+       get_editor (&xr);
 
-                       /* start a zoom drag */
+       _start_editor_x = xr;
+       _start_mouse_x = ev->x;
+       _start_mouse_y = ev->y;
+       _start_position = get_position (ev->x, ev->y);
 
-                       _zoom_position = get_position (ev->x, ev->y);
-                       _zoom_dragging = true;
-                       _editor->_dragging_playhead = true;
-                       _editor->set_follow_playhead (false);
+       if (_start_position != INSIDE && _start_position != TO_LEFT_OR_RIGHT) {
 
-                       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)) {
+               /* start a zoom_trim drag */
 
-                       /* secondary-modifier-click: locate playhead */
-                       if (_session) {
-                               _session->request_locate (ev->x / _x_scale + _start);
-                       }
+               _zoom_trim_position = get_position (ev->x, ev->y);
+               _zoom_trim_dragging = true;
+               _editor->_dragging_playhead = true;
+               _editor->set_follow_playhead (false);
 
-               } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
+               if (suspending_editor_updates ()) {
+                       get_editor (&_pending_editor_x, &_pending_editor_y);
+                       _pending_editor_changed = false;
+               }
 
-                       centre_on_click (ev);
+       } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
 
-               } else {
+               /* secondary-modifier-click: locate playhead */
+               if (_session) {
+                       _session->request_locate (ev->x / _x_scale + _start);
+               }
 
-                       /* start a move drag */
+       } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
 
-                       /* get the editor's state in case we are suspending updates */
-                       get_editor (&_pending_editor_x, &_pending_editor_y);
-                       _pending_editor_changed = false;
+               centre_on_click (ev);
 
-                       _move_dragging = true;
-                       _moved = false;
-                       _editor->_dragging_playhead = true;
-                       _editor->set_follow_playhead (false);
+       } else {
+
+               /* start a move+zoom drag */
+               get_editor (&_pending_editor_x, &_pending_editor_y);
+               _pending_editor_changed = false;
+               _editor->_dragging_playhead = true;
+               _editor->set_follow_playhead (false);
+
+               _move_dragging = true;
+
+               _last_mx = ev->x;
+               _last_my = ev->y;
+               _last_dx = 0;
+               _last_dy = 0;
+               _last_y_delta = 0;
+
+               get_window()->set_cursor (*_editor->_cursors->expand_left_right);
 
-                       ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
-               }
        }
 
        return true;
@@ -452,7 +522,7 @@ EditorSummary::on_button_press_event (GdkEventButton* ev)
 bool
 EditorSummary::suspending_editor_updates () const
 {
-       return (!Config->get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
+       return (!UIConfiguration::instance().get_update_editor_during_summary_drag () && (_zoom_trim_dragging || _move_dragging));
 }
 
 /** Fill in x and y with the editor's current viewable area in summary coordinates */
@@ -460,26 +530,27 @@ void
 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
 {
        assert (x);
-       assert (y);
-
        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.
+                * so just return where the editor will be when the drag finishes.
                */
-                  
+
                *x = _pending_editor_x;
-               *y = _pending_editor_y;
+               if (y) {
+                       *y = _pending_editor_y;
+               }
+               return;
+       }
 
-       } else {
+       /* Otherwise query the editor for its actual position */
 
-               /* Otherwise query the editor for its actual position */
+       x->first = (_editor->leftmost_sample () - _start) * _x_scale;
+       x->second = x->first + _editor->current_page_samples() * _x_scale;
 
-               x->first = (_editor->leftmost_sample () - _start) * _x_scale;
-               x->second = x->first + _editor->current_page_samples() * _x_scale;
-               
+       if (y) {
                y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
-               y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height());
+               y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y);
        }
 }
 
@@ -494,44 +565,33 @@ EditorSummary::get_position (double x, double y) const
        x_edge_size = min (x_edge_size, 8);
        x_edge_size = max (x_edge_size, 1);
 
-       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);
-       bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
        bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
-       bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
-
-       if (near_left && near_top) {
-               return LEFT_TOP;
-       } else if (near_left && near_bottom) {
-               return LEFT_BOTTOM;
-       } else if (near_right && near_top) {
-               return RIGHT_TOP;
-       } else if (near_right && near_bottom) {
-               return RIGHT_BOTTOM;
-       } else if (near_left && within_y) {
+
+       if (near_left) {
                return LEFT;
-       } else if (near_right && within_y) {
+       } else if (near_right) {
                return RIGHT;
-       } else if (near_top && within_x) {
-               return TOP;
-       } else if (near_bottom && within_x) {
-               return BOTTOM;
-       } else if (within_x && within_y) {
-               return INSIDE;
        } else if (within_x) {
-               return BELOW_OR_ABOVE;
-       } else if (within_y) {
-               return TO_LEFT_OR_RIGHT;
+               return INSIDE;
        } else {
-               return OTHERWISE_OUTSIDE;
+               return TO_LEFT_OR_RIGHT;
        }
 }
 
+void
+EditorSummary::reset_to_extents()
+{
+       /* reset as if the user never went anywhere outside the extents */
+       _leftmost = max_samplepos;
+       _rightmost = 0;
+
+       _editor->temporal_zoom_extents ();
+       set_background_dirty ();
+}
+
+
 void
 EditorSummary::set_cursor (Position p)
 {
@@ -539,100 +599,128 @@ EditorSummary::set_cursor (Position p)
        case LEFT:
                get_window()->set_cursor (*_editor->_cursors->resize_left);
                break;
-       case LEFT_TOP:
-               get_window()->set_cursor (*_editor->_cursors->resize_top_left);
-               break;
-       case TOP:
-               get_window()->set_cursor (*_editor->_cursors->resize_top);
-               break;
-       case RIGHT_TOP:
-               get_window()->set_cursor (*_editor->_cursors->resize_top_right);
-               break;
        case RIGHT:
                get_window()->set_cursor (*_editor->_cursors->resize_right);
                break;
-       case RIGHT_BOTTOM:
-               get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
-               break;
-       case BOTTOM:
-               get_window()->set_cursor (*_editor->_cursors->resize_bottom);
-               break;
-       case LEFT_BOTTOM:
-               get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
-               break;
        case INSIDE:
                get_window()->set_cursor (*_editor->_cursors->move);
                break;
        case TO_LEFT_OR_RIGHT:
-               get_window()->set_cursor (*_editor->_cursors->expand_left_right);
-               break;
-       case BELOW_OR_ABOVE:
-               get_window()->set_cursor (*_editor->_cursors->expand_up_down);
+               get_window()->set_cursor (*_editor->_cursors->move);
                break;
        default:
+               assert (0);
                get_window()->set_cursor ();
                break;
        }
 }
 
+void
+EditorSummary::summary_zoom_step (int steps /* positive steps to zoom "out" , negative steps to zoom "in" */  )
+{
+       pair<double, double> xn;
+
+       get_editor (&xn);
+
+       xn.first -= steps;
+       xn.second += steps;
+
+       /* for now, disallow really close zooming-in from the scroomer. (Currently it
+        * causes the start-offset to 'walk' because of integer limitations.
+        * To fix this, probably need to maintain float throught the get/set_editor() path.)
+        */
+       if (steps<0) {
+      if ((xn.second - xn.first) < 2)
+               return;
+       }
+
+       set_overlays_dirty ();
+       set_editor_x (xn);
+}
+
+
 bool
 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
 {
-       pair<double, double> xr = _start_editor_x;
-       pair<double, double> yr = _start_editor_y;
-       double x = _start_editor_x.first;
-       double y = _start_editor_y.first;
-
        if (_move_dragging) {
 
-               _moved = true;
+               /* To avoid accidental zooming, the mouse must move exactly vertical, not diagonal, to trigger a zoom step
+                * we use screen coordinates for this, not canvas-based grab_x */
+               double mx = ev->x;
+               double dx = mx - _last_mx;
+               double my = ev->y;
+               double dy = my - _last_my;
 
-               /* don't alter x if we clicked outside and above or below the viewbox */
-               if (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT || _start_position == OTHERWISE_OUTSIDE) {
-                       x += ev->x - _start_mouse_x;
-               }
+               /* do zooming in windowed "steps" so it feels more reversible ? */
+               const int stepsize = 2;
+               int y_delta = _start_mouse_y - my;
+               y_delta = y_delta / stepsize;
 
-               /* don't alter y if we clicked outside and to the left or right of the viewbox */
-               if (_start_position == INSIDE || _start_position == BELOW_OR_ABOVE) {
-                       y += ev->y - _start_mouse_y;
-               }
+               /* do the zoom? */
+               const float zscale = 3;
+               if ((dx == 0) && (_last_dx == 0) && (y_delta != _last_y_delta)) {
+
+                       summary_zoom_step (dy * zscale);
+
+                       /* after the zoom we must re-calculate x-pos grabs */
+                       pair<double, double> xr;
+                       get_editor (&xr);
+                       _start_editor_x = xr;
+                       _start_mouse_x = ev->x;
 
-               if (x < 0) {
-                       x = 0;
+                       _last_y_delta = y_delta;
                }
 
-               if (y < 0) {
-                       y = 0;
+               /* always track horizontal movement, if any */
+               if (dx != 0) {
+
+                       double x = _start_editor_x.first;
+                       x += ev->x - _start_mouse_x;
+
+                       if (x < 0) {
+                               x = 0;
+                       }
+
+                       /* zoom-behavior-tweaks: protect the right edge from expanding beyond the end */
+                       pair<double, double> xr;
+                       get_editor (&xr);
+                       double w = xr.second - xr.first;
+                       if (x + w < get_width()) {
+                               set_editor (x);
+                       }
                }
 
-               set_editor (x, y);
-               // set_cursor (_start_position);
+               _last_my = my;
+               _last_mx = mx;
+               _last_dx = dx;
+               _last_dy = dy;
+
+       } else if (_zoom_trim_dragging) {
 
-       } else if (_zoom_dragging) {
+               pair<double, double> xr = _start_editor_x;
 
                double const dx = ev->x - _start_mouse_x;
-               double const dy = ev->y - _start_mouse_y;
 
-               if (_zoom_position == LEFT || _zoom_position == LEFT_TOP || _zoom_position == LEFT_BOTTOM) {
+               if (_zoom_trim_position == LEFT) {
                        xr.first += dx;
-               } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) {
-                       xr.second += dx;
-               }
+               } else if (_zoom_trim_position == RIGHT) {
 
-               if (_zoom_position == TOP || _zoom_position == LEFT_TOP || _zoom_position == RIGHT_TOP) {
-                       yr.first += dy;
-               } else if (_zoom_position == BOTTOM || _zoom_position == LEFT_BOTTOM || _zoom_position == RIGHT_BOTTOM) {
-                       yr.second += dy;
+                       /* zoom-behavior-tweaks: protect the right edge from expanding beyond the edge */
+                       if ((xr.second + dx) < get_width()) {
+                               xr.second += dx;
+                       }
+
+               } else {
+                       assert (0);
+                       xr.first = -1; /* do not change */
                }
 
                set_overlays_dirty ();
-               set_cursor (_zoom_position);
-               set_editor (xr, yr);
+               set_cursor (_zoom_trim_position);
+               set_editor (xr);
 
        } else {
-
                set_cursor (get_position (ev->x, ev->y));
-
        }
 
        return true;
@@ -642,16 +730,16 @@ bool
 EditorSummary::on_button_release_event (GdkEventButton*)
 {
        bool const was_suspended = suspending_editor_updates ();
-       
+
        _move_dragging = false;
-       _zoom_dragging = false;
+       _zoom_trim_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);
+               set_editor (_pending_editor_x);
        }
-               
+
        return true;
 }
 
@@ -659,65 +747,54 @@ bool
 EditorSummary::on_scroll_event (GdkEventScroll* ev)
 {
        /* mouse wheel */
-
        pair<double, double> xr;
-       pair<double, double> yr;
-       get_editor (&xr, &yr);
+       get_editor (&xr);
        double x = xr.first;
-       double y = yr.first;
 
        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_UP: {
+
+                       summary_zoom_step (-4);
+
+                       return true;
+               } break;
+
+               case GDK_SCROLL_DOWN: {
+
+                       summary_zoom_step (4);
+
+                       return true;
+               } break;
+
                case GDK_SCROLL_LEFT:
-                       if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
+                       if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
+                               _editor->temporal_zoom_step (false);
+                       } else 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;
+                               _editor->scroll_left_half_page ();
+                               return true;
                        }
                        break;
                case GDK_SCROLL_RIGHT:
-                       if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
+                       if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
+                               _editor->temporal_zoom_step (true);
+                       } else 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;
+                               _editor->scroll_right_half_page ();
+                               return true;
                        }
                        break;
                default:
                        break;
        }
 
-       set_editor (x, y);
+       set_editor (x);
        return true;
 }
 
@@ -727,7 +804,7 @@ EditorSummary::on_scroll_event (GdkEventScroll* ev)
  *  Zoom is not changed in either direction.
  */
 void
-EditorSummary::set_editor (double const x, double const y)
+EditorSummary::set_editor (double const x)
 {
        if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) {
 
@@ -741,12 +818,11 @@ 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);
 }
 
 /** Set the editor to display a given x range and a y range with the top at a given position.
@@ -754,31 +830,16 @@ 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)
 {
        if (_editor->pending_visual_change.idle_handler_id >= 0) {
                /* see comment in other set_editor () */
                return;
        }
 
-       set_editor_x (x);
-       set_editor_y (y);
-}
-
-/** Set the editor to display given x and y ranges.  x zoom and track heights are
- *  adjusted if necessary.
- *  x and y parameters are specified in summary coordinates.
- */
-void
-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 () */
-               return;
+       if (x.first >= 0) {
+               set_editor_x (x);
        }
-
-       set_editor_x (x);
-       set_editor_y (y);
 }
 
 /** Set the left of the x range visible in the editor.
@@ -824,171 +885,30 @@ EditorSummary::set_editor_x (pair<double, double> x)
                set_dirty ();
        } else {
                _editor->reset_x_origin (x.first / _x_scale + _start);
-               
+
                double const nx = (
                        ((x.second - x.first) / _x_scale) /
                        _editor->sample_to_pixel (_editor->current_page_samples())
                        );
-               
+
                if (nx != _editor->get_current_zoom ()) {
                        _editor->reset_zoom (nx);
                }
        }
 }
 
-/** Set the top of the y range visible in the editor.
- *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
- *  @param y new editor top in summary coodinates.
- */
-void
-EditorSummary::set_editor_y (double const y)
-{
-       double y1 = summary_y_to_editor (y);
-       double const eh = _editor->visible_canvas_height();
-       double y2 = y1 + eh;
-
-       double const full_editor_height = _editor->_full_canvas_height;
-
-       if (y2 > full_editor_height) {
-               y1 -= y2 - full_editor_height;
-       }
-
-       if (y1 < 0) {
-               y1 = 0;
-       }
-
-       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,
- *  if necessary.
- *  Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0
- *  @param y new editor range in summary coodinates.
- */
-void
-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'.
-       */
-
-       /* Copy of target range for use below */
-       pair<double, double> yc = y;
-       /* Total height of all tracks */
-       double total_height = 0;
-       /* Height of any parts of tracks that aren't fully in the desired range */
-       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) {
-
-               if ((*i)->hidden()) {
-                       continue;
-               }
-
-               double const h = (*i)->effective_height ();
-               total_height += h;
-
-               if (yc.first > 0 && yc.first < _track_height) {
-                       partial_height += (_track_height - yc.first) * h / _track_height;
-               } else if (yc.first <= 0 && yc.second >= _track_height) {
-                       scale_height += h;
-               } else if (yc.second > 0 && yc.second < _track_height) {
-                       partial_height += yc.second * h / _track_height;
-                       break;
-               }
-
-               yc.first -= _track_height;
-               yc.second -= _track_height;
-       }
-
-       /* Height that we will use for scaling; use the whole editor height unless there are not
-          enough tracks to fill it.
-       */
-       double const ch = min (total_height, _editor->visible_canvas_height());
-
-       /* 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.
-       */
-       double const scale = (ch - partial_height) / scale_height;
-
-       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()) {
-                       continue;
-               }
-
-               if (yc.first <= 0 && yc.second >= _track_height) {
-                       (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)));
-               }
-
-               yc.first -= _track_height;
-               yc.second -= _track_height;
-       }
-
-       _editor->_routes->resume_redisplay ();
-
-        set_editor_y (y.first);
-}
-
 void
-EditorSummary::playhead_position_changed (framepos_t p)
+EditorSummary::playhead_position_changed (samplepos_t p)
 {
        int const o = int (_last_playhead);
-       int const n = int (playhead_frame_to_position (p));
+       int const n = int (playhead_sample_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 ());
+               set_overlays_dirty_rect (a - 2, 0, b + 2, get_height ());
        }
 }
 
-double
-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 */
-                       return ey + y * h / _track_height;
-               }
-
-               ey += h;
-               y -= _track_height;
-       }
-
-       return ey;
-}
-
 double
 EditorSummary::editor_y_to_summary (double y) const
 {
@@ -1016,35 +936,33 @@ void
 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
 {
        for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
-               /* 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 ());
+               /* Connect to the relevant signal for the route so that we know when its colour has changed */
+               (*i)->route()->presentation_info().PropertyChanged.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 (&EditorSummary::set_background_dirty, this), gui_context ());
                }
        }
 
-       _background_dirty = true;
-       set_dirty ();
+       set_background_dirty ();
 }
 
 void
-EditorSummary::route_gui_changed (string c)
+EditorSummary::route_gui_changed (PBD::PropertyChange const& what_changed)
 {
-       if (c == "color") {
-               _background_dirty = true;
-               set_dirty ();
+       if (what_changed.contains (Properties::color)) {
+               set_background_dirty ();
        }
 }
 
 double
-EditorSummary::playhead_frame_to_position (framepos_t t) const
+EditorSummary::playhead_sample_to_position (samplepos_t t) const
 {
        return (t - _start) * _x_scale;
 }
 
-framepos_t
-EditorSummary::position_to_playhead_frame_to_position (double pos) const
+samplepos_t
+EditorSummary::position_to_playhead_sample_to_position (double pos) const
 {
        return _start  + (pos * _x_scale);
 }