1 #include "ardour/session.h"
2 #include "time_axis_view.h"
3 #include "streamview.h"
4 #include "editor_summary.h"
5 #include "gui_thread.h"
7 #include "region_view.h"
8 #include "rgb_macros.h"
13 using namespace ARDOUR;
15 /** Construct an EditorSummary.
16 * @param e Editor to represent.
18 EditorSummary::EditorSummary (Editor* e)
22 _regions_dirty (true),
25 _pixels_per_frame (1),
27 _move_dragging (false),
29 _zoom_dragging (false)
39 EditorSummary::set_session (Session* s)
43 Region::RegionPropertyChanged.connect (sigc::hide (mem_fun (*this, &EditorSummary::set_dirty)));
45 _session->RegionRemoved.connect (sigc::hide (mem_fun (*this, &EditorSummary::set_dirty)));
46 _session->EndTimeChanged.connect (mem_fun (*this, &EditorSummary::set_dirty));
47 _session->StartTimeChanged.connect (mem_fun (*this, &EditorSummary::set_dirty));
53 EditorSummary::~EditorSummary ()
56 gdk_pixmap_unref (_pixmap);
60 /** Handle an expose event.
61 * @param event Event from GTK.
64 EditorSummary::on_expose_event (GdkEventExpose* event)
66 /* Render the regions pixmap */
68 Gdk::Rectangle const exposure (
69 event->area.x, event->area.y, event->area.width, event->area.height
72 Gdk::Rectangle r = exposure;
73 Gdk::Rectangle content (0, 0, _width, _height);
75 r.intersect (content, intersects);
79 GdkPixmap* p = get_pixmap (get_window()->gobj ());
83 get_style()->get_fg_gc (Gtk::STATE_NORMAL)->gobj(),
94 /* Render the view rectangle */
96 pair<double, double> x;
97 pair<double, double> y;
100 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
102 cairo_move_to (cr, x.first, y.first);
103 cairo_line_to (cr, x.second, y.first);
104 cairo_line_to (cr, x.second, y.second);
105 cairo_line_to (cr, x.first, y.second);
106 cairo_line_to (cr, x.first, y.first);
107 cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
108 cairo_fill_preserve (cr);
109 cairo_set_line_width (cr, 1);
110 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
118 /** @param drawable GDK drawable.
119 * @return pixmap for the regions.
122 EditorSummary::get_pixmap (GdkDrawable* drawable)
124 if (_regions_dirty) {
127 gdk_pixmap_unref (_pixmap);
129 _pixmap = gdk_pixmap_new (drawable, _width, _height, -1);
131 cairo_t* cr = gdk_cairo_create (_pixmap);
135 _regions_dirty = false;
141 /** Render the required regions to a cairo context.
145 EditorSummary::render (cairo_t* cr)
153 cairo_set_source_rgb (cr, 0, 0, 0);
154 cairo_rectangle (cr, 0, 0, _width, _height);
157 /* compute total height of all tracks */
161 for (PublicEditor::TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
162 int const t = (*i)->effective_height ();
164 max_height = max (max_height, t);
167 nframes_t const start = _session->current_start_frame ();
168 _pixels_per_frame = static_cast<double> (_width) / (_session->current_end_frame() - start);
169 _vertical_scale = static_cast<double> (_height) / h;
171 /* tallest a region should ever be in the summary, in pixels */
172 int const tallest_region_pixels = 12;
174 if (max_height * _vertical_scale > tallest_region_pixels) {
175 _vertical_scale = static_cast<double> (tallest_region_pixels) / max_height;
181 for (PublicEditor::TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
182 StreamView* s = (*i)->view ();
185 double const h = (*i)->effective_height () * _vertical_scale;
186 cairo_set_line_width (cr, h);
188 s->foreach_regionview (bind (
189 mem_fun (*this, &EditorSummary::render_region),
200 /** Render a region for the summary.
201 * @param r Region view.
202 * @param cr Cairo context.
203 * @param start Frame offset that the summary starts at.
204 * @param y y coordinate to render at.
207 EditorSummary::render_region (RegionView* r, cairo_t* cr, nframes_t start, double y) const
209 uint32_t const c = r->get_fill_color ();
210 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
212 cairo_move_to (cr, (r->region()->position() - start) * _pixels_per_frame, y);
213 cairo_line_to (cr, ((r->region()->position() - start + r->region()->length())) * _pixels_per_frame, y);
217 /** Set the summary so that the whole thing will be re-rendered next time it is required */
219 EditorSummary::set_dirty ()
221 ENSURE_GUI_THREAD (mem_fun (*this, &EditorSummary::set_dirty));
223 _regions_dirty = true;
227 /** Set the summary so that just the view boundary markers will be re-rendered */
229 EditorSummary::set_bounds_dirty ()
231 ENSURE_GUI_THREAD (mem_fun (*this, &EditorSummary::set_bounds_dirty));
235 /** Handle a size request.
236 * @param req GTK requisition
239 EditorSummary::on_size_request (Gtk::Requisition *req)
241 /* Use a dummy, small width and the actual height that we want */
246 /** Handle a size allocation.
247 * @param alloc GTK allocation.
250 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
252 Gtk::EventBox::on_size_allocate (alloc);
254 _width = alloc.get_width ();
255 _height = alloc.get_height ();
261 EditorSummary::centre_on_click (GdkEventButton* ev)
263 nframes_t x = (ev->x / _pixels_per_frame) + _session->current_start_frame();
264 nframes_t const xh = _editor->current_page_frames () / 2;
271 _editor->reset_x_origin (x);
273 double y = ev->y / _vertical_scale;
274 double const yh = _editor->canvas_height () / 2;
281 _editor->reset_y_origin (y);
284 /** Handle a button press.
285 * @param ev GTK event.
288 EditorSummary::on_button_press_event (GdkEventButton* ev)
290 if (ev->button == 1) {
292 pair<double, double> xr;
293 pair<double, double> yr;
294 editor_view (&xr, &yr);
296 if (xr.first <= ev->x && ev->x <= xr.second && yr.first <= ev->y && ev->y <= yr.second) {
298 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
300 /* modifier-click inside the view rectangle: start a zoom drag */
301 _zoom_position = NONE;
303 double const x1 = xr.first + (xr.second - xr.first) * 0.33;
304 double const x2 = xr.first + (xr.second - xr.first) * 0.67;
307 _zoom_position = LEFT;
308 } else if (ev->x > x2) {
309 _zoom_position = RIGHT;
311 _zoom_position = NONE;
314 if (_zoom_position != NONE) {
315 _zoom_dragging = true;
316 _mouse_x_start = ev->x;
317 _width_start = xr.second - xr.first;
318 _zoom_start = _editor->get_current_zoom ();
319 _frames_start = _editor->leftmost_position ();
320 _editor->_dragging_playhead = true;
325 /* ordinary click inside the view rectangle: start a move drag */
327 _move_dragging = true;
329 _x_offset = ev->x - xr.first;
330 _y_offset = ev->y - yr.first;
331 _editor->_dragging_playhead = true;
336 /* click outside the view rectangle: centre the view around the mouse click */
337 centre_on_click (ev);
345 EditorSummary::editor_view (pair<double, double>* x, pair<double, double>* y) const
347 x->first = (_editor->leftmost_position () - _session->current_start_frame ()) * _pixels_per_frame;
348 x->second = x->first + _editor->current_page_frames() * _pixels_per_frame;
350 y->first = _editor->get_trackview_group_vertical_offset () * _vertical_scale;
351 y->second = y->first + _editor->canvas_height () * _vertical_scale;
355 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
357 if (_move_dragging) {
360 _editor->reset_x_origin (((ev->x - _x_offset) / _pixels_per_frame) + _session->current_start_frame ());
361 _editor->reset_y_origin ((ev->y - _y_offset) / _vertical_scale);
364 } else if (_zoom_dragging) {
366 double const dx = ev->x - _mouse_x_start;
368 nframes64_t rx = _frames_start;
371 switch (_zoom_position) {
373 f = 1 - (dx / _width_start);
374 rx += (dx / _pixels_per_frame);
377 f = 1 + (dx / _width_start);
383 if (_editor->pending_visual_change.idle_handler_id < 0) {
385 /* As a side-effect, the Editor's visual change idle handler processes
386 pending GTK events. Hence this motion notify handler can be called
387 in the middle of a visual change idle handler, and if this happens,
388 the queue_visual_change calls below modify the variables that the
389 idle handler is working with. This causes problems. Hence the
390 check above. It ensures that we won't modify the pending visual change
391 while a visual change idle handler is in progress. It's not perfect,
392 as it also means that we won't change these variables if an idle handler
393 is merely pending but not executing. But c'est la vie.
396 _editor->queue_visual_change (rx);
397 _editor->queue_visual_change (_zoom_start * f);
405 EditorSummary::on_button_release_event (GdkEventButton* ev)
407 if (_move_dragging && !_moved) {
408 centre_on_click (ev);
411 _move_dragging = false;
412 _zoom_dragging = false;
413 _editor->_dragging_playhead = false;