RouteDialog: Move built-in types into template list experiment
[ardour.git] / gtk2_ardour / editor_summary.cc
index cab0f25dfcad64fcd417c127ece1a96ee12cbbbd..8d68b6775426d4199a45394ac73b50a8aa7928df 100644 (file)
@@ -46,15 +46,14 @@ EditorSummary::EditorSummary (Editor* e)
        : EditorComponent (e),
          _start (0),
          _end (1),
-         _overhang_fraction (0.1),
+         _overhang_fraction (0.02),
          _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)
@@ -113,6 +112,9 @@ EditorSummary::set_session (Session* s)
                _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 = _session->current_start_frame();
+               _rightmost = min (_session->nominal_frame_rate()*60*2, _session->current_end_frame() );  //always show at least 2 minutes
        }
 }
 
@@ -133,9 +135,25 @@ 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;
+       double theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
+       double theoretical_end = _session->current_end_frame();
+
+       /* 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 + session_length * _overhang_fraction;
+
+       /* 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;
@@ -151,13 +169,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;
@@ -169,12 +180,14 @@ 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) {
@@ -219,6 +232,19 @@ EditorSummary::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangle
                return;
        }
 
+       /* maintain the leftmost and rightmost locations that we've ever reached */
+       framecnt_t const leftmost = _editor->leftmost_sample ();
+       if ( leftmost < _leftmost) {
+               _leftmost = leftmost;
+               _background_dirty = true;
+       }
+       framecnt_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;
@@ -242,8 +268,8 @@ EditorSummary::render (Cairo::RefPtr<Cairo::Context> const& ctx, cairo_rectangle
        }
 
        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);
+       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);
 
@@ -327,9 +353,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;
 }
 
 
@@ -416,14 +442,15 @@ EditorSummary::on_button_press_event (GdkEventButton* ev)
 
        _start_editor_x = xr;
        _start_mouse_x = ev->x;
+       _start_mouse_y = ev->y;
        _start_position = get_position (ev->x, ev->y);
 
        if (_start_position != INSIDE && _start_position != TO_LEFT_OR_RIGHT) {
 
-               /* start a zoom drag */
+               /* start a zoom_trim drag */
 
-               _zoom_position = get_position (ev->x, ev->y);
-               _zoom_dragging = true;
+               _zoom_trim_position = get_position (ev->x, ev->y);
+               _zoom_trim_dragging = true;
                _editor->_dragging_playhead = true;
                _editor->set_follow_playhead (false);
 
@@ -445,16 +472,22 @@ EditorSummary::on_button_press_event (GdkEventButton* ev)
 
        } else {
 
-               /* start a move drag */
-
-               /* get the editor's state in case we are suspending updates */
+               /* start a move+zoom drag */
                get_editor (&_pending_editor_x, &_pending_editor_y);
                _pending_editor_changed = false;
-
-               _move_dragging = true;
-               _moved = 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);
+       
        }
 
        return true;
@@ -466,7 +499,7 @@ EditorSummary::on_button_press_event (GdkEventButton* ev)
 bool
 EditorSummary::suspending_editor_updates () const
 {
-       return (!UIConfiguration::instance().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 */
@@ -538,7 +571,7 @@ EditorSummary::set_cursor (Position p)
                get_window()->set_cursor (*_editor->_cursors->move);
                break;
        case TO_LEFT_OR_RIGHT:
-               get_window()->set_cursor (*_editor->_cursors->expand_left_right);
+               get_window()->set_cursor (*_editor->_cursors->move);
                break;
        default:
                assert (0);
@@ -547,32 +580,84 @@ EditorSummary::set_cursor (Position p)
        }
 }
 
-bool
-EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
+void
+EditorSummary::summary_zoom_step ( int steps /* positive steps to zoom "out" , negative steps to zoom "in" */  )
 {
-       pair<double, double> xr = _start_editor_x;
-       double x = _start_editor_x.first;
+       pair<double, double> xn;
 
-       if (_move_dragging) {
+       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;
+       }
 
-               _moved = true;
+       set_overlays_dirty ();
+       set_editor_x (xn);
+}
 
-               assert (_start_position == INSIDE || _start_position == TO_LEFT_OR_RIGHT);
-               x += ev->x - _start_mouse_x;
 
-               if (x < 0) {
-                       x = 0;
+bool
+EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
+{
+       if (_move_dragging) {
+
+               //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;
+
+               //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;
+
+               //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;
+                       
+                       _last_y_delta = y_delta;
+               }
+               
+               //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;
+                       }
+                       set_editor (x);
                }
 
-               set_editor (x);
+               _last_my = my;
+               _last_mx = mx;
+               _last_dx = dx;
+               _last_dy = dy;
 
-       } else if (_zoom_dragging) {
+       } else if (_zoom_trim_dragging) {
+
+               pair<double, double> xr = _start_editor_x;
 
                double const dx = ev->x - _start_mouse_x;
 
-               if (_zoom_position == LEFT) {
+               if (_zoom_trim_position == LEFT) {
                        xr.first += dx;
-               } else if (_zoom_position == RIGHT) {
+               } else if (_zoom_trim_position == RIGHT) {
                        xr.second += dx;
                } else {
                        assert (0);
@@ -580,11 +665,11 @@ EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
                }
 
                set_overlays_dirty ();
-               set_cursor (_zoom_position);
+               set_cursor (_zoom_trim_position);
                set_editor (xr);
 
        } else {
-               set_cursor (get_position (ev->x, ev->y));
+               set_cursor ( get_position(ev->x, ev->y) );
        }
 
        return true;
@@ -596,7 +681,7 @@ 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);
 
@@ -616,7 +701,20 @@ EditorSummary::on_scroll_event (GdkEventScroll* ev)
        double x = xr.first;
 
        switch (ev->direction) {
-               case GDK_SCROLL_UP:
+               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_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
                                _editor->temporal_zoom_step (false);
@@ -629,7 +727,6 @@ EditorSummary::on_scroll_event (GdkEventScroll* ev)
                                return true;
                        }
                        break;
-               case GDK_SCROLL_DOWN:
                case GDK_SCROLL_RIGHT:
                        if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
                                _editor->temporal_zoom_step (true);