2 Copyright (C) 2009 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 #include "ardour/session.h"
21 #include "time_axis_view.h"
22 #include "streamview.h"
23 #include "editor_summary.h"
24 #include "gui_thread.h"
26 #include "region_view.h"
27 #include "rgb_macros.h"
32 using namespace ARDOUR;
34 /** Construct an EditorSummary.
35 * @param e Editor to represent.
37 EditorSummary::EditorSummary (Editor* e)
43 _move_dragging (false),
45 _zoom_dragging (false)
55 EditorSummary::set_session (Session* s)
59 Region::RegionPropertyChanged.connect (sigc::hide (mem_fun (*this, &EditorSummary::set_dirty)));
61 _session->RegionRemoved.connect (sigc::hide (mem_fun (*this, &EditorSummary::set_dirty)));
62 _session->EndTimeChanged.connect (mem_fun (*this, &EditorSummary::set_dirty));
63 _session->StartTimeChanged.connect (mem_fun (*this, &EditorSummary::set_dirty));
64 _editor->playhead_cursor->PositionChanged.connect (mem_fun (*this, &EditorSummary::playhead_position_changed));
69 /** Handle an expose event.
70 * @param event Event from GTK.
73 EditorSummary::on_expose_event (GdkEventExpose* event)
75 CairoWidget::on_expose_event (event);
77 cairo_t* cr = gdk_cairo_create (get_window()->gobj());
79 /* Render the view rectangle */
81 pair<double, double> x;
82 pair<double, double> y;
85 cairo_move_to (cr, x.first, y.first);
86 cairo_line_to (cr, x.second, y.first);
87 cairo_line_to (cr, x.second, y.second);
88 cairo_line_to (cr, x.first, y.second);
89 cairo_line_to (cr, x.first, y.first);
90 cairo_set_source_rgba (cr, 1, 1, 1, 0.25);
91 cairo_fill_preserve (cr);
92 cairo_set_line_width (cr, 1);
93 cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
98 cairo_set_line_width (cr, 1);
99 /* XXX: colour should be set from configuration file */
100 cairo_set_source_rgba (cr, 1, 0, 0, 1);
102 double const p = _editor->playhead_cursor->current_frame * _x_scale;
103 cairo_move_to (cr, p, 0);
104 cairo_line_to (cr, p, _height);
113 /** Render the required regions to a cairo context.
117 EditorSummary::render (cairo_t* cr)
125 cairo_set_source_rgb (cr, 0, 0, 0);
126 cairo_rectangle (cr, 0, 0, _width, _height);
129 /* compute total height of all tracks */
133 for (PublicEditor::TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
134 int const t = (*i)->effective_height ();
136 max_height = max (max_height, t);
139 nframes_t const start = _session->current_start_frame ();
140 _x_scale = static_cast<double> (_width) / (_session->current_end_frame() - start);
141 _y_scale = static_cast<double> (_height) / h;
143 /* tallest a region should ever be in the summary, in pixels */
144 int const tallest_region_pixels = 4;
146 if (max_height * _y_scale > tallest_region_pixels) {
147 _y_scale = static_cast<double> (tallest_region_pixels) / max_height;
154 for (PublicEditor::TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
155 StreamView* s = (*i)->view ();
158 double const h = (*i)->effective_height () * _y_scale;
159 cairo_set_line_width (cr, h);
161 s->foreach_regionview (bind (
162 mem_fun (*this, &EditorSummary::render_region),
173 /** Render a region for the summary.
174 * @param r Region view.
175 * @param cr Cairo context.
176 * @param start Frame offset that the summary starts at.
177 * @param y y coordinate to render at.
180 EditorSummary::render_region (RegionView* r, cairo_t* cr, nframes_t start, double y) const
182 uint32_t const c = r->get_fill_color ();
183 cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
185 cairo_move_to (cr, (r->region()->position() - start) * _x_scale, y);
186 cairo_line_to (cr, ((r->region()->position() - start + r->region()->length())) * _x_scale, y);
190 /** Set the summary so that just the overlays (viewbox, playhead etc.) will be re-rendered */
192 EditorSummary::set_overlays_dirty ()
194 ENSURE_GUI_THREAD (mem_fun (*this, &EditorSummary::set_overlays_dirty));
198 /** Handle a size request.
199 * @param req GTK requisition
202 EditorSummary::on_size_request (Gtk::Requisition *req)
204 /* Use a dummy, small width and the actual height that we want */
211 EditorSummary::centre_on_click (GdkEventButton* ev)
213 pair<double, double> xr;
214 pair<double, double> yr;
215 get_editor (&xr, &yr);
217 double const w = xr.second - xr.first;
218 double const h = yr.second - yr.first;
220 xr.first = ev->x - w / 2;
221 xr.second = ev->x + w / 2;
222 yr.first = ev->y - h / 2;
223 yr.second = ev->y + h / 2;
228 } else if (xr.second > _width) {
230 xr.first = _width - w;
236 } else if (yr.second > _height) {
238 yr.first = _height - h;
244 /** Handle a button press.
245 * @param ev GTK event.
248 EditorSummary::on_button_press_event (GdkEventButton* ev)
250 if (ev->button == 1) {
252 if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
254 /* secondary-modifier-click: locate playhead */
256 _session->request_locate (ev->x / _x_scale + _session->current_start_frame());
261 pair<double, double> xr;
262 pair<double, double> yr;
263 get_editor (&xr, &yr);
265 if (xr.first <= ev->x && ev->x <= xr.second && yr.first <= ev->y && ev->y <= yr.second) {
267 _start_editor_x = xr;
268 _start_editor_y = yr;
269 _start_mouse_x = ev->x;
270 _start_mouse_y = ev->y;
272 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
274 /* modifier-click inside the view rectangle: start a zoom drag */
276 double const hx = (xr.first + xr.second) * 0.5;
277 _zoom_left = ev->x < hx;
278 _zoom_dragging = true;
279 _editor->_dragging_playhead = true;
281 /* In theory, we could support vertical dragging, which logically
282 might scale track heights in order to make the editor reflect
283 the dragged viewbox. However, having tried this:
286 c) it doesn't seem particularly useful, especially with the
287 limited height of the summary
289 So at the moment we don't support that...
294 /* ordinary click inside the view rectangle: start a move drag */
296 _move_dragging = true;
298 _editor->_dragging_playhead = true;
303 /* click outside the view rectangle: centre the view around the mouse click */
304 centre_on_click (ev);
313 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
315 x->first = (_editor->leftmost_position () - _session->current_start_frame ()) * _x_scale;
316 x->second = x->first + _editor->current_page_frames() * _x_scale;
318 y->first = _editor->vertical_adjustment.get_value() * _y_scale;
319 y->second = y->first + _editor->canvas_height () * _y_scale;
323 EditorSummary::on_motion_notify_event (GdkEventMotion* ev)
325 pair<double, double> xr = _start_editor_x;
326 pair<double, double> yr = _start_editor_y;
328 if (_move_dragging) {
332 xr.first += ev->x - _start_mouse_x;
333 xr.second += ev->x - _start_mouse_x;
334 yr.first += ev->y - _start_mouse_y;
335 yr.second += ev->y - _start_mouse_y;
339 } else if (_zoom_dragging) {
341 double const dx = ev->x - _start_mouse_x;
356 EditorSummary::on_button_release_event (GdkEventButton* ev)
358 if (_move_dragging && !_moved) {
359 centre_on_click (ev);
362 _move_dragging = false;
363 _zoom_dragging = false;
364 _editor->_dragging_playhead = false;
369 EditorSummary::on_scroll_event (GdkEventScroll* ev)
373 pair<double, double> xr;
374 pair<double, double> yr;
375 get_editor (&xr, &yr);
377 double const amount = 8;
379 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
381 if (ev->direction == GDK_SCROLL_UP) {
391 if (ev->direction == GDK_SCROLL_DOWN) {
406 EditorSummary::set_editor (pair<double,double> const & x, pair<double, double> const & y)
408 if (_editor->pending_visual_change.idle_handler_id < 0) {
410 /* As a side-effect, the Editor's visual change idle handler processes
411 pending GTK events. Hence this motion notify handler can be called
412 in the middle of a visual change idle handler, and if this happens,
413 the queue_visual_change calls below modify the variables that the
414 idle handler is working with. This causes problems. Hence the
415 check above. It ensures that we won't modify the pending visual change
416 while a visual change idle handler is in progress. It's not perfect,
417 as it also means that we won't change these variables if an idle handler
418 is merely pending but not executing. But c'est la vie.
421 _editor->reset_x_origin (x.first / _x_scale);
422 _editor->reset_y_origin (y.first / _y_scale);
425 ((x.second - x.first) / _x_scale) /
426 _editor->frame_to_unit (_editor->current_page_frames())
429 if (nx != _editor->get_current_zoom ()) {
430 _editor->reset_zoom (nx);
436 EditorSummary::playhead_position_changed (nframes64_t p)
438 if (int (p * _x_scale) != int (_last_playhead)) {
439 set_overlays_dirty ();