ardour
editor_summary.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 Paul Davis
3 
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.
8 
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.
13 
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.
17 
18 */
19 
20 #include "ardour/session.h"
21 
22 #include "canvas/debug.h"
23 
24 #include "ardour_ui.h"
25 #include "time_axis_view.h"
26 #include "streamview.h"
27 #include "editor_summary.h"
28 #include "gui_thread.h"
29 #include "editor.h"
30 #include "region_view.h"
31 #include "rgb_macros.h"
32 #include "keyboard.h"
33 #include "editor_routes.h"
34 #include "editor_cursors.h"
35 #include "mouse_cursors.h"
36 #include "route_time_axis.h"
37 
38 using namespace std;
39 using namespace ARDOUR;
41 
46  : EditorComponent (e),
47  _start (0),
48  _end (1),
49  _overhang_fraction (0.1),
50  _x_scale (1),
51  _track_height (16),
52  _last_playhead (-1),
53  _move_dragging (false),
54  _moved (false),
55  _view_rectangle_x (0, 0),
56  _view_rectangle_y (0, 0),
57  _zoom_dragging (false),
58  _old_follow_playhead (false),
59  _image (0),
60  _background_dirty (true)
61 {
62  add_events (Gdk::POINTER_MOTION_MASK|Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK|Gdk::ENTER_NOTIFY_MASK|Gdk::LEAVE_NOTIFY_MASK);
63  set_flags (get_flags() | Gtk::CAN_FOCUS);
64 
65  ARDOUR_UI::config()->ParameterChanged.connect (sigc::mem_fun (*this, &EditorSummary::parameter_changed));
66 }
67 
69 {
70  cairo_surface_destroy (_image);
71 }
72 
73 void
75 {
76 
77  if (p == "color-regions-using-track-color") {
79  }
80 }
81 
85 void
86 EditorSummary::on_size_allocate (Gtk::Allocation& alloc)
87 {
90 }
91 
92 
96 void
98 {
99  SessionHandlePtr::set_session (s);
100 
101  set_dirty ();
102 
103  /* Note: the EditorSummary already finds out about new regions from Editor::region_view_added
104  * (which attaches to StreamView::RegionViewAdded), and cut regions by the RegionPropertyChanged
105  * emitted when a cut region is added to the `cutlist' playlist.
106  */
107 
108  if (_session) {
109  Region::RegionPropertyChanged.connect (region_property_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
110  Route::RemoteControlIDChange.connect (route_ctrl_id_connection, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context());
114  _editor->selection->RegionsChanged.connect (sigc::mem_fun(*this, &EditorSummary::set_background_dirty));
115  }
116 }
117 
118 void
120 {
121  cairo_surface_destroy (_image); // passing NULL is safe
122  _image = cairo_image_surface_create (CAIRO_FORMAT_RGB24, get_width (), get_height ());
123 
124  cairo_t* cr = cairo_create (_image);
125 
126  /* background (really just the dividing lines between tracks */
127 
128  cairo_set_source_rgb (cr, 0, 0, 0);
129  cairo_rectangle (cr, 0, 0, get_width(), get_height());
130  cairo_fill (cr);
131 
132  /* compute start and end points for the summary */
133 
134  framecnt_t const session_length = _session->current_end_frame() - _session->current_start_frame ();
135  double const theoretical_start = _session->current_start_frame() - session_length * _overhang_fraction;
136  _start = theoretical_start > 0 ? theoretical_start : 0;
137  _end = _session->current_end_frame() + session_length * _overhang_fraction;
138 
139  /* compute track height */
140  int N = 0;
141  for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
142  if (!(*i)->hidden()) {
143  ++N;
144  }
145  }
146 
147  if (N == 0) {
148  _track_height = 16;
149  } else {
150  _track_height = (double) get_height() / N;
151  }
152 
153  /* calculate x scale */
154  if (_end != _start) {
155  _x_scale = static_cast<double> (get_width()) / (_end - _start);
156  } else {
157  _x_scale = 1;
158  }
159 
160  /* render tracks and regions */
161 
162  double y = 0;
163  for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
164 
165  if ((*i)->hidden()) {
166  continue;
167  }
168 
169  /* paint a non-bg colored strip to represent the track itself */
170 
171  cairo_set_source_rgb (cr, 0.2, 0.2, 0.2);
172  cairo_set_line_width (cr, _track_height - 1);
173  cairo_move_to (cr, 0, y + _track_height / 2);
174  cairo_line_to (cr, get_width(), y + _track_height / 2);
175  cairo_stroke (cr);
176 
177  StreamView* s = (*i)->view ();
178 
179  if (s) {
180  cairo_set_line_width (cr, _track_height * 0.8);
181 
182  s->foreach_regionview (sigc::bind (
183  sigc::mem_fun (*this, &EditorSummary::render_region),
184  cr,
185  y + _track_height / 2
186  ));
187  }
188 
189  y += _track_height;
190  }
191 
192  /* start and end markers */
193 
194  cairo_set_line_width (cr, 1);
195  cairo_set_source_rgb (cr, 1, 1, 0);
196 
197  const double p = (_session->current_start_frame() - _start) * _x_scale;
198  cairo_move_to (cr, p, 0);
199  cairo_line_to (cr, p, get_height());
200 
201  double const q = (_session->current_end_frame() - _start) * _x_scale;
202  cairo_move_to (cr, q, 0);
203  cairo_line_to (cr, q, get_height());
204  cairo_stroke (cr);
205 
206  cairo_destroy (cr);
207 }
208 
212 void
213 EditorSummary::render (cairo_t* cr, cairo_rectangle_t*)
214 {
215 
216  if (_session == 0) {
217  return;
218  }
219 
220  if (!_image || _background_dirty) {
222  _background_dirty = false;
223  }
224 
225  cairo_push_group (cr);
226 
227  /* Fill with the background image */
228 
229  cairo_rectangle (cr, 0, 0, get_width(), get_height());
230  cairo_set_source_surface (cr, _image, 0, 0);
231  cairo_fill (cr);
232 
233  /* Render the view rectangle. If there is an editor visual pending, don't update
234  the view rectangle now --- wait until the expose event that we'll get after
235  the visual change. This prevents a flicker.
236  */
237 
240  }
241 
242  int32_t width = _view_rectangle_x.second - _view_rectangle_x.first;
243  int32_t height = _view_rectangle_y.second - _view_rectangle_y.first;
244  cairo_rectangle (cr, _view_rectangle_x.first, _view_rectangle_y.first, width, height);
245  cairo_set_source_rgba (cr, 1, 1, 1, 0.1);
246  cairo_fill_preserve (cr);
247  cairo_set_line_width (cr, 1);
248  cairo_set_source_rgba (cr, 1, 1, 1, 0.4);
249  cairo_stroke (cr);
250 
251  /* Playhead */
252 
253  cairo_set_line_width (cr, 1);
254  /* XXX: colour should be set from configuration file */
255  cairo_set_source_rgba (cr, 1, 0, 0, 1);
256 
258  cairo_move_to (cr, ph, 0);
259  cairo_line_to (cr, ph, get_height());
260  cairo_stroke (cr);
261  cairo_pop_group_to_source (cr);
262  cairo_paint (cr);
263  _last_playhead = ph;
264 
265 }
266 
272 void
273 EditorSummary::render_region (RegionView* r, cairo_t* cr, double y) const
274 {
275  uint32_t const c = r->get_fill_color ();
276  cairo_set_source_rgb (cr, UINT_RGBA_R (c) / 255.0, UINT_RGBA_G (c) / 255.0, UINT_RGBA_B (c) / 255.0);
277 
278  if (r->region()->position() > _start) {
279  cairo_move_to (cr, (r->region()->position() - _start) * _x_scale, y);
280  } else {
281  cairo_move_to (cr, 0, y);
282  }
283 
284  if ((r->region()->position() + r->region()->length()) > _start) {
285  cairo_line_to (cr, ((r->region()->position() - _start + r->region()->length())) * _x_scale, y);
286  } else {
287  cairo_line_to (cr, 0, y);
288  }
289 
290  cairo_stroke (cr);
291 }
292 
293 void
295 {
296  _background_dirty = true;
297  set_dirty ();
298 }
299 
301 void
303 {
305  queue_draw ();
306 }
307 
309 void
310 EditorSummary::set_overlays_dirty (int x, int y, int w, int h)
311 {
313  queue_draw_area (x, y, w, h);
314 }
315 
316 
320 void
321 EditorSummary::on_size_request (Gtk::Requisition *req)
322 {
323  /* Use a dummy, small width and the actual height that we want */
324  req->width = 64;
325  req->height = 32;
326 }
327 
328 
329 void
330 EditorSummary::centre_on_click (GdkEventButton* ev)
331 {
332  pair<double, double> xr;
333  pair<double, double> yr;
334  get_editor (&xr, &yr);
335 
336  double const w = xr.second - xr.first;
337  double ex = ev->x - w / 2;
338  if (ex < 0) {
339  ex = 0;
340  } else if ((ex + w) > get_width()) {
341  ex = get_width() - w;
342  }
343 
344  double const h = yr.second - yr.first;
345  double ey = ev->y - h / 2;
346  if (ey < 0) {
347  ey = 0;
348  } else if ((ey + h) > get_height()) {
349  ey = get_height() - h;
350  }
351 
352  set_editor (ex, ey);
353 }
354 
355 bool
357 {
358  grab_focus ();
359  Keyboard::magic_widget_grab_focus ();
360  return false;
361 }
362 
363 bool
365 {
366  /* there are no inferior/child windows, so any leave event means that
367  we're gone.
368  */
369  Keyboard::magic_widget_drop_focus ();
370  return false;
371 }
372 
373 bool
375 {
376  gint x, y;
377  GtkAccelKey set_playhead_accel;
378  if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
379  if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
380  if (_session) {
381  get_pointer (x, y);
383  return true;
384  }
385  }
386  }
387 
388  return false;
389 }
390 
391 bool
393 {
394 
395  GtkAccelKey set_playhead_accel;
396  if (gtk_accel_map_lookup_entry ("<Actions>/Editor/set-playhead", &set_playhead_accel)) {
397  if (key->keyval == set_playhead_accel.accel_key && (int) key->state == set_playhead_accel.accel_mods) {
398  return true;
399  }
400  }
401  return false;
402 }
403 
407 bool
409 {
411 
412  if (ev->button == 1) {
413 
414  pair<double, double> xr;
415  pair<double, double> yr;
416  get_editor (&xr, &yr);
417 
418  _start_editor_x = xr;
419  _start_editor_y = yr;
420  _start_mouse_x = ev->x;
421  _start_mouse_y = ev->y;
422  _start_position = get_position (ev->x, ev->y);
423 
426  ) {
427 
428  /* start a zoom drag */
429 
430  _zoom_position = get_position (ev->x, ev->y);
431  _zoom_dragging = true;
432  _editor->_dragging_playhead = true;
433  _editor->set_follow_playhead (false);
434 
435  if (suspending_editor_updates ()) {
437  _pending_editor_changed = false;
438  }
439 
440  } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier)) {
441 
442  /* secondary-modifier-click: locate playhead */
443  if (_session) {
444  _session->request_locate (ev->x / _x_scale + _start);
445  }
446 
447  } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
448 
449  centre_on_click (ev);
450 
451  } else {
452 
453  /* start a move drag */
454 
455  /* get the editor's state in case we are suspending updates */
457  _pending_editor_changed = false;
458 
459  _move_dragging = true;
460  _moved = false;
461  _editor->_dragging_playhead = true;
462  _editor->set_follow_playhead (false);
463 
464  ArdourCanvas::checkpoint ("sum", "------------------ summary move drag starts.\n");
465  }
466  }
467 
468  return true;
469 }
470 
474 bool
476 {
477  return (!ARDOUR_UI::config()->get_update_editor_during_summary_drag () && (_zoom_dragging || _move_dragging));
478 }
479 
481 void
482 EditorSummary::get_editor (pair<double, double>* x, pair<double, double>* y) const
483 {
484  assert (x);
485  assert (y);
486 
487  if (suspending_editor_updates ()) {
488 
489  /* We are dragging, and configured not to update the editor window during drags,
490  so just return where the editor will be when the drag finishes.
491  */
492 
493  *x = _pending_editor_x;
494  *y = _pending_editor_y;
495 
496  } else {
497 
498  /* Otherwise query the editor for its actual position */
499 
500  x->first = (_editor->leftmost_sample () - _start) * _x_scale;
501  x->second = x->first + _editor->current_page_samples() * _x_scale;
502 
503  y->first = editor_y_to_summary (_editor->vertical_adjustment.get_value ());
504  y->second = editor_y_to_summary (_editor->vertical_adjustment.get_value () + _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y);
505  }
506 }
507 
510 EditorSummary::get_position (double x, double y) const
511 {
512  /* how close the mouse has to be to the edge of the view rectangle to be considered `on it',
513  in pixels */
514 
515  int x_edge_size = (_view_rectangle_x.second - _view_rectangle_x.first) / 4;
516  x_edge_size = min (x_edge_size, 8);
517  x_edge_size = max (x_edge_size, 1);
518 
519  int y_edge_size = (_view_rectangle_y.second - _view_rectangle_y.first) / 4;
520  y_edge_size = min (y_edge_size, 8);
521  y_edge_size = max (y_edge_size, 1);
522 
523  bool const near_left = (std::abs (x - _view_rectangle_x.first) < x_edge_size);
524  bool const near_right = (std::abs (x - _view_rectangle_x.second) < x_edge_size);
525  bool const near_top = (std::abs (y - _view_rectangle_y.first) < y_edge_size);
526  bool const near_bottom = (std::abs (y - _view_rectangle_y.second) < y_edge_size);
527  bool const within_x = _view_rectangle_x.first < x && x < _view_rectangle_x.second;
528  bool const within_y = _view_rectangle_y.first < y && y < _view_rectangle_y.second;
529 
530  if (near_left && near_top) {
531  return LEFT_TOP;
532  } else if (near_left && near_bottom) {
533  return LEFT_BOTTOM;
534  } else if (near_right && near_top) {
535  return RIGHT_TOP;
536  } else if (near_right && near_bottom) {
537  return RIGHT_BOTTOM;
538  } else if (near_left && within_y) {
539  return LEFT;
540  } else if (near_right && within_y) {
541  return RIGHT;
542  } else if (near_top && within_x) {
543  return TOP;
544  } else if (near_bottom && within_x) {
545  return BOTTOM;
546  } else if (within_x && within_y) {
547  return INSIDE;
548  } else if (within_x) {
549  return BELOW_OR_ABOVE;
550  } else if (within_y) {
551  return TO_LEFT_OR_RIGHT;
552  } else {
553  return OTHERWISE_OUTSIDE;
554  }
555 }
556 
557 void
559 {
560  switch (p) {
561  case LEFT:
562  get_window()->set_cursor (*_editor->_cursors->resize_left);
563  break;
564  case LEFT_TOP:
565  get_window()->set_cursor (*_editor->_cursors->resize_top_left);
566  break;
567  case TOP:
568  get_window()->set_cursor (*_editor->_cursors->resize_top);
569  break;
570  case RIGHT_TOP:
571  get_window()->set_cursor (*_editor->_cursors->resize_top_right);
572  break;
573  case RIGHT:
574  get_window()->set_cursor (*_editor->_cursors->resize_right);
575  break;
576  case RIGHT_BOTTOM:
577  get_window()->set_cursor (*_editor->_cursors->resize_bottom_right);
578  break;
579  case BOTTOM:
580  get_window()->set_cursor (*_editor->_cursors->resize_bottom);
581  break;
582  case LEFT_BOTTOM:
583  get_window()->set_cursor (*_editor->_cursors->resize_bottom_left);
584  break;
585  case INSIDE:
586  get_window()->set_cursor (*_editor->_cursors->move);
587  break;
588  case TO_LEFT_OR_RIGHT:
589  get_window()->set_cursor (*_editor->_cursors->expand_left_right);
590  break;
591  case BELOW_OR_ABOVE:
592  get_window()->set_cursor (*_editor->_cursors->expand_up_down);
593  break;
594  default:
595  get_window()->set_cursor ();
596  break;
597  }
598 }
599 
600 bool
602 {
603  pair<double, double> xr = _start_editor_x;
604  pair<double, double> yr = _start_editor_y;
605  double x = _start_editor_x.first;
606  double y = _start_editor_y.first;
607 
608  if (_move_dragging) {
609 
610  _moved = true;
611 
612  /* don't alter x if we clicked outside and above or below the viewbox */
614  x += ev->x - _start_mouse_x;
615  }
616 
617  /* don't alter y if we clicked outside and to the left or right of the viewbox */
619  y += ev->y - _start_mouse_y;
620  }
621 
622  if (x < 0) {
623  x = 0;
624  }
625 
626  if (y < 0) {
627  y = 0;
628  }
629 
630  set_editor (x, y);
631  // set_cursor (_start_position);
632 
633  } else if (_zoom_dragging) {
634 
635  double const dx = ev->x - _start_mouse_x;
636  double const dy = ev->y - _start_mouse_y;
637 
639  xr.first += dx;
641  xr.second += dx;
642  } else {
643  xr.first = -1; /* do not change */
644  }
645 
647  yr.first += dy;
649  yr.second += dy;
650  } else {
651  yr.first = -1; /* do not change y */
652  }
653 
656  set_editor (xr, yr);
657 
658  } else {
659 
660  set_cursor (get_position (ev->x, ev->y));
661 
662  }
663 
664  return true;
665 }
666 
667 bool
669 {
670  bool const was_suspended = suspending_editor_updates ();
671 
672  _move_dragging = false;
673  _zoom_dragging = false;
674  _editor->_dragging_playhead = false;
676 
677  if (was_suspended && _pending_editor_changed) {
679  }
680 
681  return true;
682 }
683 
684 bool
685 EditorSummary::on_scroll_event (GdkEventScroll* ev)
686 {
687  /* mouse wheel */
688 
689  pair<double, double> xr;
690  pair<double, double> yr;
691  get_editor (&xr, &yr);
692  double x = xr.first;
693  double y = yr.first;
694 
695  switch (ev->direction) {
696  case GDK_SCROLL_UP:
697  if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
698  x -= 64;
699  } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
700  _editor->temporal_zoom_step (false);
701  } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
702  yr.first += 4;
703  yr.second -= 4;
704  set_editor (xr, yr);
705  return true;
706  } else {
707  y -= 8;
708  }
709  break;
710  case GDK_SCROLL_DOWN:
711  if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollHorizontalModifier)) {
712  x += 64;
713  } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomHorizontalModifier)) {
714  _editor->temporal_zoom_step (true);
715  } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::ScrollZoomVerticalModifier)) {
716  yr.first -= 4;
717  yr.second += 4;
718  set_editor (xr, yr);
719  return true;
720  } else {
721  y += 8;
722  }
723  break;
724  case GDK_SCROLL_LEFT:
725  if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
726  x -= 64;
727  } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
728  x -= 1;
729  } else {
730  x -= 8;
731  }
732  break;
733  case GDK_SCROLL_RIGHT:
734  if (Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier)) {
735  x += 64;
736  } else if (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier)) {
737  x += 1;
738  } else {
739  x += 8;
740  }
741  break;
742  default:
743  break;
744  }
745 
746  set_editor (x, y);
747  return true;
748 }
749 
755 void
756 EditorSummary::set_editor (double const x, double const y)
757 {
759 
760  /* As a side-effect, the Editor's visual change idle handler processes
761  pending GTK events. Hence this motion notify handler can be called
762  in the middle of a visual change idle handler, and if this happens,
763  the queue_visual_change calls below modify the variables that the
764  idle handler is working with. This causes problems. Hence this
765  check. It ensures that we won't modify the pending visual change
766  while a visual change idle handler is in progress. It's not perfect,
767  as it also means that we won't change these variables if an idle handler
768  is merely pending but not executing. But c'est la vie.
769  */
770 
771  return;
772  }
773 
774  set_editor_x (x);
775  set_editor_y (y);
776 }
777 
782 void
783 EditorSummary::set_editor (pair<double,double> const x, double const y)
784 {
786  /* see comment in other set_editor () */
787  return;
788  }
789 
790  set_editor_x (x);
791  set_editor_y (y);
792 }
793 
798 void
799 EditorSummary::set_editor (pair<double,double> const x, pair<double, double> const y)
800 {
802  /* see comment in other set_editor () */
803  return;
804  }
805 
806  if (x.first >= 0) {
807  set_editor_x (x);
808  }
809  if (y.first >= 0) {
810  set_editor_y (y);
811  }
812 }
813 
818 void
820 {
821  if (x < 0) {
822  x = 0;
823  }
824 
825  if (suspending_editor_updates ()) {
826  double const w = _pending_editor_x.second - _pending_editor_x.first;
827  _pending_editor_x.first = x;
828  _pending_editor_x.second = x + w;
830  set_dirty ();
831  } else {
833  }
834 }
835 
840 void
841 EditorSummary::set_editor_x (pair<double, double> x)
842 {
843  if (x.first < 0) {
844  x.first = 0;
845  }
846 
847  if (x.second < 0) {
848  x.second = x.first + 1;
849  }
850 
851  if (suspending_editor_updates ()) {
852  _pending_editor_x = x;
854  set_dirty ();
855  } else {
856  _editor->reset_x_origin (x.first / _x_scale + _start);
857 
858  double const nx = (
859  ((x.second - x.first) / _x_scale) /
861  );
862 
863  if (nx != _editor->get_current_zoom ()) {
864  _editor->reset_zoom (nx);
865  }
866  }
867 }
868 
873 void
875 {
876  double y1 = summary_y_to_editor (y);
877  double const eh = _editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y;
878  double y2 = y1 + eh;
879 
880  double const full_editor_height = _editor->_full_canvas_height;
881 
882  if (y2 > full_editor_height) {
883  y1 -= y2 - full_editor_height;
884  }
885 
886  if (y1 < 0) {
887  y1 = 0;
888  }
889 
890  if (suspending_editor_updates ()) {
891  double const h = _pending_editor_y.second - _pending_editor_y.first;
892  _pending_editor_y.first = y;
893  _pending_editor_y.second = y + h;
895  set_dirty ();
896  } else {
897  _editor->reset_y_origin (y1);
898  }
899 }
900 
906 void
907 EditorSummary::set_editor_y (pair<double, double> const y)
908 {
909  if (suspending_editor_updates ()) {
910  _pending_editor_y = y;
912  set_dirty ();
913  return;
914  }
915 
916  /* Compute current height of tracks between y.first and y.second. We add up
917  the total height into `total_height' and the height of complete tracks into
918  `scale height'.
919  */
920 
921  /* Copy of target range for use below */
922  pair<double, double> yc = y;
923  /* Total height of all tracks */
924  double total_height = 0;
925  /* Height of any parts of tracks that aren't fully in the desired range */
926  double partial_height = 0;
927  /* Height of any tracks that are fully in the desired range */
928  double scale_height = 0;
929 
931 
932  for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
933 
934  if ((*i)->hidden()) {
935  continue;
936  }
937 
938  double const h = (*i)->effective_height ();
939  total_height += h;
940 
941  if (yc.first > 0 && yc.first < _track_height) {
942  partial_height += (_track_height - yc.first) * h / _track_height;
943  } else if (yc.first <= 0 && yc.second >= _track_height) {
944  scale_height += h;
945  } else if (yc.second > 0 && yc.second < _track_height) {
946  partial_height += yc.second * h / _track_height;
947  break;
948  }
949 
950  yc.first -= _track_height;
951  yc.second -= _track_height;
952  }
953 
954  /* Height that we will use for scaling; use the whole editor height unless there are not
955  enough tracks to fill it.
956  */
957  double const ch = min (total_height, (_editor->visible_canvas_height() - _editor->get_trackview_group()->canvas_origin().y));
958 
959  /* hence required scale factor of the complete tracks to fit the required y range;
960  the amount of space they should take up divided by the amount they currently take up.
961  */
962  double const scale = (ch - partial_height) / scale_height;
963 
964  yc = y;
965 
966  /* Scale complete tracks within the range to make it fit */
967 
968  for (TrackViewList::const_iterator i = _editor->track_views.begin(); i != _editor->track_views.end(); ++i) {
969 
970  if ((*i)->hidden()) {
971  continue;
972  }
973 
974  if (yc.first <= 0 && yc.second >= _track_height) {
975  (*i)->set_height (max (TimeAxisView::preset_height (HeightSmall), (uint32_t) ((*i)->effective_height() * scale)), TimeAxisView::TotalHeight);
976  }
977 
978  yc.first -= _track_height;
979  yc.second -= _track_height;
980  }
981 
983 
984  set_editor_y (y.first);
985 }
986 
987 void
989 {
990  int const o = int (_last_playhead);
991  int const n = int (playhead_frame_to_position (p));
992  if (_session && o != n) {
993  int a = max(2, min (o, n));
994  int b = max (o, n);
995  set_overlays_dirty (a - 2, 0, b + 2, get_height ());
996  }
997 }
998 
999 double
1001 {
1002  double ey = 0;
1003  for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1004 
1005  if ((*i)->hidden()) {
1006  continue;
1007  }
1008 
1009  double const h = (*i)->effective_height ();
1010  if (y < _track_height) {
1011  /* in this track */
1012  return ey + y * h / _track_height;
1013  }
1014 
1015  ey += h;
1016  y -= _track_height;
1017  }
1018 
1019  return ey;
1020 }
1021 
1022 double
1024 {
1025  double sy = 0;
1026  for (TrackViewList::const_iterator i = _editor->track_views.begin (); i != _editor->track_views.end(); ++i) {
1027 
1028  if ((*i)->hidden()) {
1029  continue;
1030  }
1031 
1032  double const h = (*i)->effective_height ();
1033  if (y < h) {
1034  /* in this track */
1035  return sy + y * _track_height / h;
1036  }
1037 
1038  sy += _track_height;
1039  y -= h;
1040  }
1041 
1042  return sy;
1043 }
1044 
1045 void
1046 EditorSummary::routes_added (list<RouteTimeAxisView*> const & r)
1047 {
1048  for (list<RouteTimeAxisView*>::const_iterator i = r.begin(); i != r.end(); ++i) {
1049  /* Connect to gui_changed() on the route so that we know when their colour has changed */
1050  (*i)->route()->gui_changed.connect (*this, invalidator (*this), boost::bind (&EditorSummary::route_gui_changed, this, _1), gui_context ());
1052  if (tr) {
1053  tr->PlaylistChanged.connect (*this, invalidator (*this), boost::bind (&EditorSummary::set_background_dirty, this), gui_context ());
1054  }
1055  }
1056 
1058 }
1059 
1060 void
1062 {
1063  if (c == "color") {
1065  }
1066 }
1067 
1068 double
1070 {
1071  return (t - _start) * _x_scale;
1072 }
1073 
1074 framepos_t
1076 {
1077  return _start + (pos * _x_scale);
1078 }
bool transport_rolling() const
Definition: session.h:592
Gdk::Cursor * resize_left
Definition: mouse_cursors.h:64
bool follow_playhead() const
Definition: editor.h:342
void reset_y_origin(double)
Definition: editor.cc:4371
void set_cursor(Position)
Gdk::Cursor * resize_bottom
Definition: mouse_cursors.h:70
VisualChange pending_visual_change
Definition: editor.h:1127
#define UINT_RGBA_R(x)
Definition: rgb_macros.h:53
Selection * selection
Definition: editor.h:1801
void set_follow_playhead(bool yn, bool catch_up=true)
Definition: editor.cc:4004
std::pair< double, double > _start_editor_y
double summary_y_to_editor(double) const
std::pair< double, double > _pending_editor_y
EditorRoutes * _routes
Definition: editor.h:1862
EditorSummary(Editor *)
double _overhang_fraction
bool on_scroll_event(GdkEventScroll *)
static PBD::Signal1< void, framepos_t > EndTimeChanged
Definition: session.h:585
shared_ptr< T > dynamic_pointer_cast(shared_ptr< U > const &r)
Definition: shared_ptr.hpp:396
void centre_on_click(GdkEventButton *)
double _start_mouse_y
static int N
Definition: signals_test.cc:27
Gtk::Adjustment vertical_adjustment
Definition: editor.h:1052
Definition: Beats.hpp:239
cairo_surface_t * _image
void render_region(RegionView *, cairo_t *, double) const
Gdk::Cursor * move
Definition: mouse_cursors.h:72
double playhead_frame_to_position(framepos_t) const
ArdourCanvas::Container * get_trackview_group() const
Definition: editor.h:507
void temporal_zoom_step(bool coarser)
Definition: editor_ops.cc:1628
PBD::ScopedConnectionList position_connection
void request_locate(framepos_t frame, bool with_roll=false)
EditorCursor * playhead_cursor
Definition: editor.h:1011
double _start_mouse_x
void render(cairo_t *, cairo_rectangle_t *)
#define ENSURE_GUI_THREAD(obj, method,...)
Definition: gui_thread.h:34
#define invalidator(x)
Definition: gui_thread.h:40
bool _old_follow_playhead
bool on_enter_notify_event(GdkEventCrossing *)
double editor_y_to_summary(double) const
PBD::Signal1< void, framepos_t > PositionChanged
bool _pending_editor_changed
void set_editor_y(double)
double _full_canvas_height
full height of the canvas
Definition: editor.h:1075
double _last_playhead
bool on_key_press_event(GdkEventKey *)
void parameter_changed(std::string)
static PBD::Signal1< void, framepos_t > StartTimeChanged
Definition: session.h:584
bool _dragging_playhead
Definition: editor.h:1512
void set_editor(double, double)
LIBGTKMM2EXT_API uint64_t Keyboard
Definition: debug.cc:23
framepos_t current_frame() const
framepos_t _end
end frame of the overview
Position _zoom_position
void foreach_regionview(sigc::slot< void, RegionView * > slot)
Definition: streamview.cc:506
void on_size_request(Gtk::Requisition *)
void routes_added(std::list< RouteTimeAxisView * > const &)
int64_t framecnt_t
Definition: types.h:76
double visible_canvas_height() const
Definition: editor.h:152
void render_background_image()
boost::shared_ptr< ARDOUR::Region > region() const
Definition: region_view.h:66
#define UINT_RGBA_G(x)
Definition: rgb_macros.h:54
Gdk::Cursor * resize_top_left
Definition: mouse_cursors.h:65
void reset_zoom(framecnt_t)
Definition: editor.cc:4379
void playhead_position_changed(framepos_t)
void set_background_dirty()
PBD::ScopedConnection route_ctrl_id_connection
Definition: amp.h:29
std::pair< double, double > _pending_editor_x
double _x_scale
pixels per frame for the x axis of the pixmap
#define gui_context()
Definition: gui_thread.h:36
framecnt_t get_current_zoom() const
Definition: editor.h:290
Gdk::Cursor * expand_up_down
Definition: mouse_cursors.h:74
void set_dirty()
Gdk::Cursor * resize_top
Definition: mouse_cursors.h:66
void route_gui_changed(std::string)
void suspend_redisplay()
Definition: editor_routes.h:42
int64_t framepos_t
Definition: types.h:66
uint32_t get_fill_color() const
Definition: region_view.cc:532
PBD::ScopedConnectionList _session_connections
Gdk::Cursor * resize_top_right
Definition: mouse_cursors.h:67
PBD::Signal0< void > PlaylistChanged
Definition: track.h:166
bool on_leave_notify_event(GdkEventCrossing *)
framepos_t position() const
Definition: region.h:112
framepos_t position_to_playhead_frame_to_position(double pos) const
double sample_to_pixel(framepos_t sample) const
Definition: editor.h:234
std::pair< double, double > _start_editor_x
Gdk::Cursor * resize_bottom_right
Definition: mouse_cursors.h:69
Definition: editor.h:134
framecnt_t current_page_samples() const
Definition: editor.h:148
framepos_t current_end_frame() const
Definition: session.cc:5048
std::pair< double, double > _view_rectangle_x
framepos_t leftmost_sample() const
Definition: editor.h:146
framepos_t current_start_frame() const
Definition: session.cc:5042
static UIConfiguration * config()
Definition: ardour_ui.h:188
double _track_height
bool on_button_press_event(GdkEventButton *)
bool on_motion_notify_event(GdkEventMotion *)
bool on_button_release_event(GdkEventButton *)
Gdk::Cursor * resize_right
Definition: mouse_cursors.h:68
void set_session(ARDOUR::Session *)
Gdk::Cursor * resize_bottom_left
Definition: mouse_cursors.h:71
TrackViewList track_views
Definition: editor.h:1135
sigc::signal< void, std::string > ParameterChanged
Definition: ui_config.h:78
std::pair< double, double > _view_rectangle_y
Position get_position(double, double) const
void reset_x_origin(framepos_t)
Definition: editor.cc:4363
framecnt_t length() const
Definition: region.h:114
T * get_pointer(shared_ptr< T > const &p)
Definition: shared_ptr.hpp:426
static uint32_t preset_height(Height)
void get_editor(std::pair< double, double > *, std::pair< double, double > *) const
sigc::signal< void > RegionsChanged
Definition: selection.h:97
MouseCursors * _cursors
Definition: editor.h:2209
void on_size_allocate(Gtk::Allocation &alloc)
framepos_t _start
start frame of the overview
void set_overlays_dirty()
PBD::ScopedConnectionList region_property_connection
ARDOUR::Session * _session
#define UINT_RGBA_B(x)
Definition: rgb_macros.h:55
bool suspending_editor_updates() const
Gdk::Cursor * expand_left_right
Definition: mouse_cursors.h:73
Position _start_position
void on_size_allocate(Gtk::Allocation &)
bool on_key_release_event(GdkEventKey *)
void set_editor_x(double)
void resume_redisplay()
Definition: editor_routes.h:49