X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Feditor_summary.cc;h=a2882a201a25a1fbc9aea895fc0b52b39abd7f02;hb=87c38e1a269e09b40e8e8866961498c36ce95fbb;hp=c097bfe049b939ac8cf1602b04bb1e31b814d2cf;hpb=73192bc1a7ea55fa1864dc3826845b15c00dd2ec;p=ardour.git diff --git a/gtk2_ardour/editor_summary.cc b/gtk2_ardour/editor_summary.cc index c097bfe049..a2882a201a 100644 --- a/gtk2_ardour/editor_summary.cc +++ b/gtk2_ardour/editor_summary.cc @@ -18,6 +18,9 @@ */ #include "ardour/session.h" + +#include "canvas/debug.h" + #include "time_axis_view.h" #include "streamview.h" #include "editor_summary.h" @@ -29,6 +32,8 @@ #include "editor_routes.h" #include "editor_cursors.h" #include "mouse_cursors.h" +#include "route_time_axis.h" +#include "ui_config.h" using namespace std; using namespace ARDOUR; @@ -49,14 +54,42 @@ EditorSummary::EditorSummary (Editor* e) _moved (false), _view_rectangle_x (0, 0), _view_rectangle_y (0, 0), - _zoom_dragging (false) + _zoom_dragging (false), + _old_follow_playhead (false), + _image (0), + _background_dirty (true) { - 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()); + 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); - add_events (Gdk::POINTER_MOTION_MASK); + 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. + * @param alloc GTK allocation. + */ +void +EditorSummary::on_size_allocate (Gtk::Allocation& alloc) +{ + CairoWidget::on_size_allocate (alloc); + set_background_dirty (); +} + + /** Connect to a session. * @param s Session. */ @@ -73,80 +106,31 @@ 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()); + Region::RegionPropertyChanged.connect (region_property_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 (&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)); } } -/** Handle an expose event. - * @param event Event from GTK. - */ -bool -EditorSummary::on_expose_event (GdkEventExpose* event) +void +EditorSummary::render_background_image () { - 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); - } - - 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); + cairo_surface_destroy (_image); // passing NULL is safe + _image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, get_width (), get_height ()); - /* Playhead */ + cairo_t* cr = cairo_create (_image); - 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. - * @param cr Context. - */ -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) { - return; - } - /* 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; @@ -159,16 +143,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 (_width) / (_end - _start); + _x_scale = static_cast (get_width()) / (_end - _start); } else { _x_scale = 1; } @@ -182,16 +166,18 @@ 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) { - 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), @@ -199,7 +185,7 @@ EditorSummary::render (cairo_t* cr) y + _track_height / 2 )); } - + y += _track_height; } @@ -208,15 +194,74 @@ 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_stroke (cr); + 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, _height); + cairo_line_to (cr, q, get_height()); cairo_stroke (cr); + + cairo_destroy (cr); +} + +/** Render the required regions to a cairo context. + * @param cr Context. + */ +void +EditorSummary::render (cairo_t* cr, cairo_rectangle_t*) +{ + + if (_session == 0) { + return; + } + + if (!_image || _background_dirty) { + render_background_image (); + _background_dirty = false; + } + + cairo_push_group (cr); + + /* Fill with the background image */ + + 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. + */ + + 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.1); + cairo_fill_preserve (cr); + cairo_set_line_width (cr, 1); + cairo_set_source_rgba (cr, 1, 1, 1, 0.4); + 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); + cairo_pop_group_to_source (cr); + cairo_paint (cr); + _last_playhead = ph; + } /** Render a region for the summary. @@ -245,14 +290,32 @@ EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const cairo_stroke (cr); } +void +EditorSummary::set_background_dirty () +{ + if (!_background_dirty) { + _background_dirty = true; + set_dirty (); + } +} + /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */ 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 */ @@ -273,25 +336,71 @@ EditorSummary::centre_on_click (GdkEventButton* ev) get_editor (&xr, &yr); double const w = xr.second - xr.first; - - xr.first = ev->x - w / 2; - xr.second = ev->x + w / 2; - - if (xr.first < 0) { - xr.first = 0; - xr.second = w; - } else if (xr.second > _width) { - xr.second = _width; - xr.first = _width - w; + double ex = ev->x - w / 2; + if (ex < 0) { + ex = 0; + } else if ((ex + w) > get_width()) { + ex = get_width() - w; } - double ey = summary_y_to_editor (ev->y); - ey -= (_editor->canvas_height() - _editor->get_canvas_timebars_vsize ()) / 2; + 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); +} + +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 ("/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 ("/Editor/set-playhead", &set_playhead_accel)) { + if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) { + return true; + } } - - set_editor (xr, editor_y_to_summary (ey)); + return false; } /** Handle a button press. @@ -300,6 +409,8 @@ EditorSummary::centre_on_click (GdkEventButton* ev) bool EditorSummary::on_button_press_event (GdkEventButton* ev) { + _old_follow_playhead = _editor->follow_playhead (); + if (ev->button == 1) { pair xr; @@ -321,6 +432,12 @@ EditorSummary::on_button_press_event (GdkEventButton* ev) _zoom_position = get_position (ev->x, ev->y); _zoom_dragging = true; _editor->_dragging_playhead = true; + _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)) { @@ -337,27 +454,57 @@ 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; + _editor->set_follow_playhead (false); + + ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n"); } } 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 (!UIConfiguration::instance().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* x, pair* 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_sample () - _start) * _x_scale; + x->second = x->first + _editor->current_page_samples() * _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->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y); + } } /** Get an expression of the position of a point with respect to the view rectangle */ @@ -374,7 +521,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); @@ -457,6 +604,7 @@ EditorSummary::on_motion_notify_event (GdkEventMotion* ev) { pair xr = _start_editor_x; pair yr = _start_editor_y; + double x = _start_editor_x.first; double y = _start_editor_y.first; if (_move_dragging) { @@ -465,8 +613,7 @@ EditorSummary::on_motion_notify_event (GdkEventMotion* ev) /* 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) { - xr.first += ev->x - _start_mouse_x; - xr.second += ev->x - _start_mouse_x; + x += ev->x - _start_mouse_x; } /* don't alter y if we clicked outside and to the left or right of the viewbox */ @@ -474,17 +621,16 @@ EditorSummary::on_motion_notify_event (GdkEventMotion* ev) y += ev->y - _start_mouse_y; } - if (xr.first < 0) { - xr.second -= xr.first; - xr.first = 0; + if (x < 0) { + x = 0; } if (y < 0) { y = 0; } - set_editor (xr, y); - set_cursor (_start_position); + set_editor (x, y); + // set_cursor (_start_position); } else if (_zoom_dragging) { @@ -495,12 +641,16 @@ EditorSummary::on_motion_notify_event (GdkEventMotion* ev) xr.first += dx; } else if (_zoom_position == RIGHT || _zoom_position == RIGHT_TOP || _zoom_position == RIGHT_BOTTOM) { xr.second += dx; + } else { + xr.first = -1; /* do not change */ } 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; + } else { + yr.first = -1; /* do not change y */ } set_overlays_dirty (); @@ -519,9 +669,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; } @@ -533,55 +691,77 @@ EditorSummary::on_scroll_event (GdkEventScroll* ev) pair xr; pair yr; get_editor (&xr, &yr); + 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) { - xr.first += amount; - xr.second += amount; - } else if (ev->direction == GDK_SCROLL_DOWN) { - xr.first -= amount; - xr.second -= 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) { - xr.first -= amount; - xr.second -= amount; - } else if (ev->direction == GDK_SCROLL_RIGHT) { - xr.first += amount; - xr.second += amount; - } + switch (ev->direction) { + case GDK_SCROLL_UP: + if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) { + _editor->scroll_left_half_page (); + return true; + } 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)) { + _editor->scroll_right_half_page (); + return true; + } 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 { + _editor->scroll_left_half_page (); + return true; + } + 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 { + _editor->scroll_right_half_page (); + return true; + } + break; + default: + break; } - set_editor (xr, y); + set_editor (x, y); return true; } -/** Set the editor to display a given x range and a y range with the top at a given position. - * The editor's x zoom is adjusted if necessary, but the y zoom is not changed. +/** Set the editor to display a x range with the left at a given position + * and a y range with the top at a given position. * x and y parameters are specified in summary coordinates. + * Zoom is not changed in either direction. */ void -EditorSummary::set_editor (pair const & x, double const y) +EditorSummary::set_editor (double const x, double const y) { - if (_editor->pending_visual_change.idle_handler_id >= 0) { + if (_editor->pending_visual_change.idle_handler_id >= 0 && _editor->pending_visual_change.being_handled == true) { /* As a side-effect, the Editor's visual change idle handler processes pending GTK events. Hence this motion notify handler can be called @@ -596,44 +776,100 @@ EditorSummary::set_editor (pair const & x, double const y) 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. +/** Set the editor to display a given x range and a y range with the top at a given position. + * The editor's x zoom is adjusted if necessary, but the y zoom is not changed. * x and y parameters are specified in summary coordinates. */ void -EditorSummary::set_editor (pair const & x, pair const & y) +EditorSummary::set_editor (pair 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); } +/** 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 const x, pair 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); + } + if (y.first >= 0) { + set_editor_y (y); + } +} + +/** Set the left of the x range visible in the editor. + * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0 + * @param x new x left position in summary coordinates. + */ +void +EditorSummary::set_editor_x (double x) +{ + 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. * Caller should have checked that Editor::pending_visual_change.idle_handler_id is < 0 * @param x new x range in summary coordinates. */ void -EditorSummary::set_editor_x (pair const & x) +EditorSummary::set_editor_x (pair x) { - _editor->reset_x_origin (x.first / _x_scale + _start); + if (x.first < 0) { + x.first = 0; + } + + 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->sample_to_pixel (_editor->current_page_samples()) + ); - 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 (nx != _editor->get_current_zoom ()) { + _editor->reset_zoom (nx); + } + } } /** Set the top of the y range visible in the editor. @@ -644,20 +880,28 @@ void 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 const eh = _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y; double y2 = y1 + eh; - - double const full_editor_height = _editor->full_canvas_height - _editor->get_canvas_timebars_vsize(); + + double const full_editor_height = _editor->_full_canvas_height; 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, @@ -666,16 +910,29 @@ EditorSummary::set_editor_y (double const y) * @param y new editor range in summary coodinates. */ void -EditorSummary::set_editor_y (pair const & y) +EditorSummary::set_editor_y (pair 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 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) { @@ -683,38 +940,45 @@ EditorSummary::set_editor_y (pair const & y) if ((*i)->hidden()) { continue; } - + double const h = (*i)->effective_height (); + total_height += h; - if (yc.first >= 0 && yc.first < _track_height) { - total_height += (_track_height - yc.first) * h / _track_height; - } else if (yc.first < 0 && yc.second > _track_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) { - total_height += yc.second * h / _track_height; + } 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; } - - /* hence required scale factor of the complete tracks to fit the required y range */ - double const scale = ((_editor->canvas_height() - _editor->get_canvas_timebars_vsize()) - (total_height - scale_height)) / scale_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() - _editor->get_trackview_group()->canvas_origin().y)); + + /* 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))); + if (yc.first <= 0 && yc.second >= _track_height) { + (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)), TimeAxisView::TotalHeight); } yc.first -= _track_height; @@ -722,15 +986,19 @@ EditorSummary::set_editor_y (pair 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 ()); } } @@ -739,11 +1007,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 */ @@ -762,7 +1030,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; } @@ -779,3 +1047,38 @@ EditorSummary::editor_y_to_summary (double y) const return sy; } + +void +EditorSummary::routes_added (list const & r) +{ + for (list::const_iterator i = r.begin(); i != r.end(); ++i) { + /* 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 tr = boost::dynamic_pointer_cast ((*i)->route ()); + if (tr) { + tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ()); + } + } + + set_background_dirty (); +} + +void +EditorSummary::route_gui_changed (PBD::PropertyChange const& what_changed) +{ + if (what_changed.contains (Properties::color)) { + set_background_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); +}