#include "canvas/debug.h"
+#include <gtkmm/menu.h>
+#include <gtkmm/menuitem.h>
+
#include "time_axis_view.h"
#include "streamview.h"
#include "editor_summary.h"
#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;
: 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),
- _old_follow_playhead (false)
+ _zoom_trim_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());
- 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());
-
+ 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.
+ * @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.
*/
*/
if (_session) {
- _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());
+ 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));
}
+
+ _leftmost = max_samplepos;
+ _rightmost = 0;
}
-/** Render the required regions to a cairo context.
- * @param cr Context.
- */
void
-EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
+EditorSummary::render_background_image ()
{
+ 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 */
cairo_set_source_rgb (cr, 0, 0, 0);
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;
+ 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;
_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;
/* 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;
cairo_set_line_width (cr, 1);
cairo_set_source_rgb (cr, 1, 1, 0);
- const double p = (_session->current_start_frame() - _start) * _x_scale;
+ const double p = (_session->current_start_sample() - _start) * _x_scale;
cairo_move_to (cr, p, 0);
cairo_line_to (cr, p, get_height());
- cairo_stroke (cr);
- double const q = (_session->current_end_frame() - _start) * _x_scale;
+ 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);
+}
+
+/** Render the required regions to a cairo context.
+ * @param cr Context.
+ */
+void
+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);
+
+ /* 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.
- */
+ * 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);
+ int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
+ 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 */
/* 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);
+ cairo_pop_group_to_source (cr);
+ cairo_paint (cr);
_last_playhead = ph;
}
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 ()
/** 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);
}
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;
}
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;
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 ();
return false;
}
-bool
+bool
EditorSummary::on_leave_notify_event (GdkEventCrossing*)
{
/* there are no inferior/child windows, so any leave event means that
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;
}
}
return false;
}
+#include "gtkmm2ext/utils.h"
+
/** Handle a button press.
* @param ev GTK event.
*/
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;
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 */
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);
}
}
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)
{
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);
- if (x < 0) {
- x = 0;
+ /* 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;
}
- 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_dragging) {
+ } else if (_zoom_trim_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) {
+
+ /* zoom-behavior-tweaks: protect the right edge from expanding beyond the edge */
+ if ((xr.second + dx) < get_width()) {
+ xr.second += dx;
+ }
- 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 {
+ 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;
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;
}
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;
}
* 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) {
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.
* 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.
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
{
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 (&CairoWidget::set_dirty, this), gui_context ());
+ tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
}
}
- set_dirty ();
+ set_background_dirty ();
}
void
-EditorSummary::route_gui_changed (string c)
+EditorSummary::route_gui_changed (PBD::PropertyChange const& what_changed)
{
- if (c == "color") {
- 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);
}