ardour
editor_rulers.cc
Go to the documentation of this file.
1 /*
2  Copyright (C) 2000 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 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23 
24 #include <cstdio> // for sprintf, grrr
25 #include <cmath>
26 #include <inttypes.h>
27 
28 #include <string>
29 
30 #include <gtk/gtkaction.h>
31 
32 #include "canvas/container.h"
33 #include "canvas/canvas.h"
34 #include "canvas/ruler.h"
35 #include "canvas/debug.h"
36 #include "canvas/scroll_group.h"
37 
38 #include "ardour/session.h"
39 #include "ardour/tempo.h"
40 #include "ardour/profile.h"
41 
42 #include "gtkmm2ext/gtk_ui.h"
43 #include "gtkmm2ext/keyboard.h"
44 
45 #include "ardour_ui.h"
46 #include "editor.h"
47 #include "editing.h"
48 #include "actions.h"
49 #include "gui_thread.h"
50 #include "ruler_dialog.h"
51 #include "time_axis_view.h"
52 #include "editor_drag.h"
53 #include "editor_cursors.h"
54 
55 #include "i18n.h"
56 
57 using namespace ARDOUR;
58 using namespace PBD;
59 using namespace Gtk;
60 using namespace Editing;
61 
62 /* the order here must match the "metric" enums in editor.h */
63 
64 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
65 {
66  public:
67  TimecodeMetric (Editor* e) : _editor (e) {}
68 
69  void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
70  _editor->metric_get_timecode (marks, lower, upper, maxchars);
71  }
72 
73  private:
75 };
76 
77 class SamplesMetric : public ArdourCanvas::Ruler::Metric
78 {
79  public:
80  SamplesMetric (Editor* e) : _editor (e) {}
81 
82  void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
83  _editor->metric_get_samples (marks, lower, upper, maxchars);
84  }
85 
86  private:
88 };
89 
90 class BBTMetric : public ArdourCanvas::Ruler::Metric
91 {
92  public:
93  BBTMetric (Editor* e) : _editor (e) {}
94 
95  void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
96  _editor->metric_get_bbt (marks, lower, upper, maxchars);
97  }
98 
99  private:
101 };
102 
103 class MinsecMetric : public ArdourCanvas::Ruler::Metric
104 {
105  public:
106  MinsecMetric (Editor* e) : _editor (e) {}
107 
108  void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
109  _editor->metric_get_minsec (marks, lower, upper, maxchars);
110  }
111 
112  private:
114 };
115 
116 static ArdourCanvas::Ruler::Metric* _bbt_metric;
117 static ArdourCanvas::Ruler::Metric* _timecode_metric;
118 static ArdourCanvas::Ruler::Metric* _samples_metric;
119 static ArdourCanvas::Ruler::Metric* _minsec_metric;
120 
121 void
123 {
124  ruler_grabbed_widget = 0;
125 
126  Pango::FontDescription font (ARDOUR_UI::config()->get_SmallerFont());
127 
128  _timecode_metric = new TimecodeMetric (this);
129  _bbt_metric = new BBTMetric (this);
130  _minsec_metric = new MinsecMetric (this);
131  _samples_metric = new SamplesMetric (this);
132 
133  timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
134  ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
135  timecode_ruler->set_font_description (font);
136  CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
137  timecode_nmarks = 0;
138 
139  samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
140  ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
141  samples_ruler->set_font_description (font);
142  CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
143 
144  minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
145  ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
146  minsec_ruler->set_font_description (font);
147  CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
148  minsec_nmarks = 0;
149 
150  bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
151  ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
152  bbt_ruler->set_font_description (font);
153  CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
154  timecode_nmarks = 0;
155 
156  using namespace Box_Helpers;
157  BoxList & lab_children = time_bars_vbox.children();
158 
159  lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
160  lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
161  lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
162  lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
163  lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
164  lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
165  lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
166  lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
167  lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
168  lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
169  lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
170 
171  /* 1 event handler to bind them all ... */
172 
173  timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
174  minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
175  bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
176  samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
177 
178  visible_timebars = 0; /*this will be changed below */
179 }
180 
181 bool
183 {
185  if (!ruler_dialog) {
186  ruler_dialog = new RulerDialog ();
187  }
188  ruler_dialog->present ();
189  }
190 
191  return true;
192 }
193 
194 void
196 {
197  using namespace Menu_Helpers;
198 
199  if (editor_ruler_menu == 0) {
200  editor_ruler_menu = new Menu;
201  editor_ruler_menu->set_name ("ArdourContextMenu");
202  }
203 
204  // always build from scratch
205  MenuList& ruler_items = editor_ruler_menu->items();
206  editor_ruler_menu->set_name ("ArdourContextMenu");
207  ruler_items.clear();
208 
209  switch (t) {
210  case MarkerBarItem:
211  ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false)));
212  ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
213  ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
214  ruler_items.push_back (SeparatorElem ());
215  break;
216  case RangeMarkerBarItem:
217  ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
218  ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
219  ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
220  ruler_items.push_back (SeparatorElem ());
221 
222  break;
224  ruler_items.push_back (MenuElem (_("New Loop range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_loop), where)));
225  ruler_items.push_back (MenuElem (_("New Punch range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_punch), where)));
226  break;
227 
228  case CdMarkerBarItem:
229  // TODO
230  ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true)));
231  break;
232 
233 
234  case TempoBarItem:
235  ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
236  break;
237 
238  case MeterBarItem:
239  ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
240  break;
241 
242  case VideoBarItem:
243  /* proper headings would be nice
244  * but AFAICT the only way to get them will be to define a
245  * special GTK style for insensitive Elements or subclass MenuItem
246  */
247  //ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
248  //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
249  ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
250  if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
251  ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
252  if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
253  ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
254  if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
255 
256  ruler_items.push_back (SeparatorElem ());
257 
258  //ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
259  //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
260  ruler_items.push_back (CheckMenuElem (_("Lock")));
261  {
262  Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
263  vtl_lock->set_active(is_video_timeline_locked());
264  vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
265  }
266 
267  ruler_items.push_back (SeparatorElem ());
268 
269  //ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
270  //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
271  ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
272  {
273  Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
275  xjadeo_toggle->set_sensitive(false);
276  }
277  xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
278  xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
279  }
280  break;
281 
282  default:
283  break;
284  }
285 
286  if (!ruler_items.empty()) {
287  editor_ruler_menu->popup (1, gtk_get_current_event_time());
288  }
289 
290  no_ruler_shown_update = false;
291 }
292 
293 void
295 {
296  XMLNode* node = new XMLNode(X_("RulerVisibility"));
297 
298  node->add_property (X_("timecode"), ruler_timecode_action->get_active() ? "yes": "no");
299  node->add_property (X_("bbt"), ruler_bbt_action->get_active() ? "yes": "no");
300  node->add_property (X_("samples"), ruler_samples_action->get_active() ? "yes": "no");
301  node->add_property (X_("minsec"), ruler_minsec_action->get_active() ? "yes": "no");
302  node->add_property (X_("tempo"), ruler_tempo_action->get_active() ? "yes": "no");
303  node->add_property (X_("meter"), ruler_meter_action->get_active() ? "yes": "no");
304  node->add_property (X_("marker"), ruler_marker_action->get_active() ? "yes": "no");
305  node->add_property (X_("rangemarker"), ruler_range_action->get_active() ? "yes": "no");
306  node->add_property (X_("transportmarker"), ruler_loop_punch_action->get_active() ? "yes": "no");
307  node->add_property (X_("cdmarker"), ruler_cd_marker_action->get_active() ? "yes": "no");
308  node->add_property (X_("videotl"), ruler_video_action->get_active() ? "yes": "no");
309 
310  _session->add_extra_xml (*node);
311  _session->set_dirty ();
312 }
313 
314 void
316 {
317  XMLProperty* prop;
318  XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
319 
320  no_ruler_shown_update = true;
321 
322  if (node) {
323  if ((prop = node->property ("timecode")) != 0) {
324  if (string_is_affirmative (prop->value())) {
325  ruler_timecode_action->set_active (true);
326  } else {
327  ruler_timecode_action->set_active (false);
328  }
329  }
330  if ((prop = node->property ("bbt")) != 0) {
331  if (string_is_affirmative (prop->value())) {
332  ruler_bbt_action->set_active (true);
333  } else {
334  ruler_bbt_action->set_active (false);
335  }
336  }
337  if ((prop = node->property ("samples")) != 0) {
338  if (string_is_affirmative (prop->value())) {
339  ruler_samples_action->set_active (true);
340  } else {
341  ruler_samples_action->set_active (false);
342  }
343  }
344  if ((prop = node->property ("minsec")) != 0) {
345  if (string_is_affirmative (prop->value())) {
346  ruler_minsec_action->set_active (true);
347  } else {
348  ruler_minsec_action->set_active (false);
349  }
350  }
351  if ((prop = node->property ("tempo")) != 0) {
352  if (string_is_affirmative (prop->value())) {
353  ruler_tempo_action->set_active (true);
354  } else {
355  ruler_tempo_action->set_active (false);
356  }
357  }
358  if ((prop = node->property ("meter")) != 0) {
359  if (string_is_affirmative (prop->value())) {
360  ruler_meter_action->set_active (true);
361  } else {
362  ruler_meter_action->set_active (false);
363  }
364  }
365  if ((prop = node->property ("marker")) != 0) {
366  if (string_is_affirmative (prop->value())) {
367  ruler_marker_action->set_active (true);
368  } else {
369  ruler_marker_action->set_active (false);
370  }
371  }
372  if ((prop = node->property ("rangemarker")) != 0) {
373  if (string_is_affirmative (prop->value())) {
374  ruler_range_action->set_active (true);
375  } else {
376  ruler_range_action->set_active (false);
377  }
378  }
379 
380  if ((prop = node->property ("transportmarker")) != 0) {
381  if (string_is_affirmative (prop->value())) {
382  ruler_loop_punch_action->set_active (true);
383  } else {
384  ruler_loop_punch_action->set_active (false);
385  }
386  }
387 
388  if ((prop = node->property ("cdmarker")) != 0) {
389  if (string_is_affirmative (prop->value())) {
390  ruler_cd_marker_action->set_active (true);
391  } else {
392  ruler_cd_marker_action->set_active (false);
393  }
394 
395  } else {
396  // this _session doesn't yet know about the cdmarker ruler
397  // as a benefit to the user who doesn't know the feature exists, show the ruler if
398  // any cd marks exist
399  ruler_cd_marker_action->set_active (false);
400  const Locations::LocationList & locs = _session->locations()->list();
401  for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
402  if ((*i)->is_cd_marker()) {
403  ruler_cd_marker_action->set_active (true);
404  break;
405  }
406  }
407  }
408 
409  if ((prop = node->property ("videotl")) != 0) {
410  if (string_is_affirmative (prop->value())) {
411  ruler_video_action->set_active (true);
412  } else {
413  ruler_video_action->set_active (false);
414  }
415  }
416 
417  }
418 
419  no_ruler_shown_update = false;
420  update_ruler_visibility ();
421 }
422 
423 void
425 {
426  int visible_timebars = 0;
427 
428  if (no_ruler_shown_update) {
429  return;
430  }
431 
432  /* the order of the timebars is fixed, so we have to go through each one
433  * and adjust its position depending on what is shown.
434  *
435  * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
436  * loop/punch, cd markers, location markers
437  */
438 
439  double tbpos = 0.0;
440  double tbgpos = 0.0;
441  double old_unit_pos;
442 
443 #ifdef GTKOSX
444  /* gtk update probs require this (damn) */
445  meter_label.hide();
446  tempo_label.hide();
447  range_mark_label.hide();
448  transport_mark_label.hide();
449  cd_mark_label.hide();
450  mark_label.hide();
451  videotl_label.hide();
452 #endif
453 
454  if (ruler_minsec_action->get_active()) {
455  old_unit_pos = minsec_ruler->position().y;
456  if (tbpos != old_unit_pos) {
457  minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
458  }
459  minsec_ruler->show();
460  minsec_label.show();
461  tbpos += timebar_height;
462  tbgpos += timebar_height;
463  visible_timebars++;
464  } else {
465  minsec_ruler->hide();
466  minsec_label.hide();
467  }
468 
469  if (ruler_timecode_action->get_active()) {
470  old_unit_pos = timecode_ruler->position().y;
471  if (tbpos != old_unit_pos) {
472  timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
473  }
474  timecode_ruler->show();
475  timecode_label.show();
476  tbpos += timebar_height;
477  tbgpos += timebar_height;
478  visible_timebars++;
479  } else {
480  timecode_ruler->hide();
481  timecode_label.hide();
482  }
483 
484  if (ruler_samples_action->get_active()) {
485  old_unit_pos = samples_ruler->position().y;
486  if (tbpos != old_unit_pos) {
487  samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
488  }
489  samples_ruler->show();
490  samples_label.show();
491  tbpos += timebar_height;
492  tbgpos += timebar_height;
493  visible_timebars++;
494  } else {
495  samples_ruler->hide();
496  samples_label.hide();
497  }
498 
499  if (ruler_bbt_action->get_active()) {
500  old_unit_pos = bbt_ruler->position().y;
501  if (tbpos != old_unit_pos) {
502  bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
503  }
504  bbt_ruler->show();
505  bbt_label.show();
506  tbpos += timebar_height;
507  tbgpos += timebar_height;
508  visible_timebars++;
509  } else {
510  bbt_ruler->hide();
511  bbt_label.hide();
512  }
513 
514  if (ruler_meter_action->get_active()) {
515  old_unit_pos = meter_group->position().y;
516  if (tbpos != old_unit_pos) {
517  meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
518  }
519  meter_group->show();
520  meter_label.show();
521  tbpos += timebar_height;
522  tbgpos += timebar_height;
523  visible_timebars++;
524  } else {
525  meter_group->hide();
526  meter_label.hide();
527  }
528 
529  if (ruler_tempo_action->get_active()) {
530  old_unit_pos = tempo_group->position().y;
531  if (tbpos != old_unit_pos) {
532  tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
533  }
534  tempo_group->show();
535  tempo_label.show();
536  tbpos += timebar_height;
537  tbgpos += timebar_height;
538  visible_timebars++;
539  } else {
540  tempo_group->hide();
541  tempo_label.hide();
542  }
543 
544  if (!Profile->get_sae() && ruler_range_action->get_active()) {
545  old_unit_pos = range_marker_group->position().y;
546  if (tbpos != old_unit_pos) {
547  range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
548  }
549  range_marker_group->show();
550  range_mark_label.show();
551 
552  tbpos += timebar_height;
553  tbgpos += timebar_height;
554  visible_timebars++;
555  } else {
556  range_marker_group->hide();
557  range_mark_label.hide();
558  }
559 
560  if (ruler_loop_punch_action->get_active()) {
561  old_unit_pos = transport_marker_group->position().y;
562  if (tbpos != old_unit_pos) {
563  transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
564  }
565  transport_marker_group->show();
566  transport_mark_label.show();
567  tbpos += timebar_height;
568  tbgpos += timebar_height;
569  visible_timebars++;
570  } else {
571  transport_marker_group->hide();
572  transport_mark_label.hide();
573  }
574 
575  if (ruler_cd_marker_action->get_active()) {
576  old_unit_pos = cd_marker_group->position().y;
577  if (tbpos != old_unit_pos) {
578  cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
579  }
580  cd_marker_group->show();
581  cd_mark_label.show();
582  tbpos += timebar_height;
583  tbgpos += timebar_height;
584  visible_timebars++;
585  // make sure all cd markers show up in their respective places
586  update_cd_marker_display();
587  } else {
588  cd_marker_group->hide();
589  cd_mark_label.hide();
590  // make sure all cd markers show up in their respective places
591  update_cd_marker_display();
592  }
593 
594  if (ruler_marker_action->get_active()) {
595  old_unit_pos = marker_group->position().y;
596  if (tbpos != old_unit_pos) {
597  marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
598  }
599  marker_group->show();
600  mark_label.show();
601  tbpos += timebar_height;
602  tbgpos += timebar_height;
603  visible_timebars++;
604  } else {
605  marker_group->hide();
606  mark_label.hide();
607  }
608 
609  if (ruler_video_action->get_active()) {
610  old_unit_pos = videotl_group->position().y;
611  if (tbpos != old_unit_pos) {
612  videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
613  }
614  videotl_group->show();
615  videotl_label.show();
616  tbpos += timebar_height * videotl_bar_height;
617  tbgpos += timebar_height * videotl_bar_height;
618  visible_timebars+=videotl_bar_height;
619  queue_visual_videotimeline_update();
620  } else {
621  videotl_group->hide();
622  videotl_label.hide();
623  update_video_timeline(true);
624  }
625 
626  time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
627 
628  /* move hv_scroll_group (trackviews) to the end of the timebars
629  */
630 
631  hv_scroll_group->set_y_position (timebar_height * visible_timebars);
632 
633  compute_fixed_ruler_scale ();
634  update_fixed_rulers();
635  redisplay_tempo (false);
636 
637  /* Changing ruler visibility means that any lines on markers might need updating */
638  for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
639  i->second->setup_lines ();
640  }
641 }
642 
643 void
645 {
647 
648  if (_session == 0) {
649  return;
650  }
651 
652  framepos_t rightmost_frame = leftmost_frame + current_page_samples();
653 
654  if (ruler_timecode_action->get_active()) {
655  timecode_ruler->set_range (leftmost_frame, rightmost_frame);
656  }
657 }
658 
659 void
661 {
662  if (_session == 0) {
663  return;
664  }
665 
666  if (ruler_timecode_action->get_active()) {
667  set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
668  }
669 
670  if (ruler_minsec_action->get_active()) {
671  set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
672  }
673 
674  if (ruler_samples_action->get_active()) {
675  set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
676  }
677 }
678 
679 void
681 {
682  framepos_t rightmost_frame;
683 
684  if (_session == 0) {
685  return;
686  }
687 
688  compute_fixed_ruler_scale ();
689 
690  _timecode_metric->units_per_pixel = samples_per_pixel;
691  _samples_metric->units_per_pixel = samples_per_pixel;
692  _minsec_metric->units_per_pixel = samples_per_pixel;
693 
694  rightmost_frame = leftmost_frame + current_page_samples();
695 
696  /* these force a redraw, which in turn will force execution of the metric callbacks
697  to compute the relevant ticks to display.
698  */
699 
700  if (ruler_timecode_action->get_active()) {
701  timecode_ruler->set_range (leftmost_frame, rightmost_frame);
702  }
703 
704  if (ruler_samples_action->get_active()) {
705  samples_ruler->set_range (leftmost_frame, rightmost_frame);
706  }
707 
708  if (ruler_minsec_action->get_active()) {
709  minsec_ruler->set_range (leftmost_frame, rightmost_frame);
710  }
711 }
712 
713 void
714 Editor::update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
715  ARDOUR::TempoMap::BBTPointList::const_iterator& end)
716 {
717  if (_session == 0) {
718  return;
719  }
720 
721  compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(),
722  begin, end);
723 
724  _bbt_metric->units_per_pixel = samples_per_pixel;
725 
726  if (ruler_bbt_action->get_active()) {
727  bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
728  }
729 }
730 
731 
732 void
734 {
735  using namespace std;
736 
737  framepos_t spacer;
738  framepos_t fr;
739 
740  if (_session == 0) {
741  return;
742  }
743 
744  fr = _session->frame_rate();
745 
746  if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
747  lower = lower - spacer;
748  } else {
749  lower = 0;
750  }
751 
752  upper = upper + spacer;
753  framecnt_t const range = upper - lower;
754 
755  if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
756  timecode_ruler_scale = timecode_show_bits;
757  timecode_mark_modulo = 20;
758  timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
759  } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
760  timecode_ruler_scale = timecode_show_frames;
761  timecode_mark_modulo = 1;
762  timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
763  } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
764  timecode_ruler_scale = timecode_show_frames;
765  timecode_mark_modulo = 2;
766  timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
767  } else if (range <= fr) { /* 0.5-1 second */
768  timecode_ruler_scale = timecode_show_frames;
769  timecode_mark_modulo = 5;
770  timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
771  } else if (range <= 2 * fr) { /* 1-2 seconds */
772  timecode_ruler_scale = timecode_show_frames;
773  timecode_mark_modulo = 10;
774  timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
775  } else if (range <= 8 * fr) { /* 2-8 seconds */
776  timecode_ruler_scale = timecode_show_seconds;
777  timecode_mark_modulo = 1;
778  timecode_nmarks = 2 + (range / fr);
779  } else if (range <= 16 * fr) { /* 8-16 seconds */
780  timecode_ruler_scale = timecode_show_seconds;
781  timecode_mark_modulo = 2;
782  timecode_nmarks = 2 + (range / fr);
783  } else if (range <= 30 * fr) { /* 16-30 seconds */
784  timecode_ruler_scale = timecode_show_seconds;
785  timecode_mark_modulo = 5;
786  timecode_nmarks = 2 + (range / fr);
787  } else if (range <= 60 * fr) { /* 30-60 seconds */
788  timecode_ruler_scale = timecode_show_seconds;
789  timecode_mark_modulo = 5;
790  timecode_nmarks = 2 + (range / fr);
791  } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
792  timecode_ruler_scale = timecode_show_seconds;
793  timecode_mark_modulo = 15;
794  timecode_nmarks = 2 + (range / fr);
795  } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
796  timecode_ruler_scale = timecode_show_seconds;
797  timecode_mark_modulo = 30;
798  timecode_nmarks = 2 + (range / fr);
799  } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
800  timecode_ruler_scale = timecode_show_minutes;
801  timecode_mark_modulo = 2;
802  timecode_nmarks = 2 + 10;
803  } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
804  timecode_ruler_scale = timecode_show_minutes;
805  timecode_mark_modulo = 5;
806  timecode_nmarks = 2 + 30;
807  } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
808  timecode_ruler_scale = timecode_show_minutes;
809  timecode_mark_modulo = 10;
810  timecode_nmarks = 2 + 60;
811  } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
812  timecode_ruler_scale = timecode_show_minutes;
813  timecode_mark_modulo = 30;
814  timecode_nmarks = 2 + (60 * 4);
815  } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
816  timecode_ruler_scale = timecode_show_hours;
817  timecode_mark_modulo = 1;
818  timecode_nmarks = 2 + 8;
819  } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
820  timecode_ruler_scale = timecode_show_hours;
821  timecode_mark_modulo = 1;
822  timecode_nmarks = 2 + 24;
823  } else {
824 
825  const framecnt_t hours_in_range = range / (60 * 60 * fr);
826  const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
827 
828  /* Normally we do not need to know anything about the width of the canvas
829  to set the ruler scale, because the caller has already determined
830  the width and set lower + upper arguments to this function to match that.
831 
832  But in this case, where the range defined by lower and uppper can vary
833  substantially (basically anything from 24hrs+ to several billion years)
834  trying to decide which tick marks to show does require us to know
835  about the available width.
836  */
837 
838  timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
839  timecode_ruler_scale = timecode_show_many_hours;
840  timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
841  }
842 }
843 
844 void
845 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
846 {
847  framepos_t pos;
848  framecnt_t spacer;
849  Timecode::Time timecode;
850  gchar buf[16];
851  gint n;
852  ArdourCanvas::Ruler::Mark mark;
853 
854  if (_session == 0) {
855  return;
856  }
857 
858  if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
859  lower = lower - spacer;
860  } else {
861  lower = 0;
862  }
863 
864  pos = (framecnt_t) floor (lower);
865 
866  switch (timecode_ruler_scale) {
867  case timecode_show_bits:
868  // Find timecode time of this sample (pos) with subframe accuracy
869  _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
870  for (n = 0; n < timecode_nmarks; n++) {
871  _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
872  if ((timecode.subframes % timecode_mark_modulo) == 0) {
873  if (timecode.subframes == 0) {
874  mark.style = ArdourCanvas::Ruler::Mark::Major;
875  snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
876  } else {
877  mark.style = ArdourCanvas::Ruler::Mark::Minor;
878  snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
879  }
880  } else {
881  snprintf (buf, sizeof(buf)," ");
882  mark.style = ArdourCanvas::Ruler::Mark::Micro;
883  }
884  mark.label = buf;
885  mark.position = pos;
886  marks.push_back (mark);
887  // Increment subframes by one
888  Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
889  }
890  break;
891 
892  case timecode_show_frames:
893  // Find timecode time of this sample (pos)
894  _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
895  // Go to next whole frame down
896  Timecode::frames_floor( timecode );
897  for (n = 0; n < timecode_nmarks; n++) {
898  _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
899  if ((timecode.frames % timecode_mark_modulo) == 0) {
900  if (timecode.frames == 0) {
901  mark.style = ArdourCanvas::Ruler::Mark::Major;
902  } else {
903  mark.style = ArdourCanvas::Ruler::Mark::Minor;
904  }
905  mark.position = pos;
906  snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
907  } else {
908  snprintf (buf, sizeof(buf)," ");
909  mark.style = ArdourCanvas::Ruler::Mark::Micro;
910  mark.position = pos;
911  }
912  mark.label = buf;
913  marks.push_back (mark);
914  Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
915  }
916  break;
917 
918  case timecode_show_seconds:
919  // Find timecode time of this sample (pos)
920  _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
921  // Go to next whole second down
922  Timecode::seconds_floor( timecode );
923  for (n = 0; n < timecode_nmarks; n++) {
924  _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
925  if ((timecode.seconds % timecode_mark_modulo) == 0) {
926  if (timecode.seconds == 0) {
927  mark.style = ArdourCanvas::Ruler::Mark::Major;
928  mark.position = pos;
929  } else {
930  mark.style = ArdourCanvas::Ruler::Mark::Minor;
931  mark.position = pos;
932  }
933  snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
934  } else {
935  snprintf (buf, sizeof(buf)," ");
936  mark.style = ArdourCanvas::Ruler::Mark::Micro;
937  mark.position = pos;
938  }
939  mark.label = buf;
940  marks.push_back (mark);
941  Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
942  }
943  break;
944 
945  case timecode_show_minutes:
946  //Find timecode time of this sample (pos)
947  _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
948  // Go to next whole minute down
949  Timecode::minutes_floor( timecode );
950  for (n = 0; n < timecode_nmarks; n++) {
951  _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
952  if ((timecode.minutes % timecode_mark_modulo) == 0) {
953  if (timecode.minutes == 0) {
954  mark.style = ArdourCanvas::Ruler::Mark::Major;
955  } else {
956  mark.style = ArdourCanvas::Ruler::Mark::Minor;
957  }
958  snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
959  } else {
960  snprintf (buf, sizeof(buf)," ");
961  mark.style = ArdourCanvas::Ruler::Mark::Micro;
962  }
963  mark.label = buf;
964  mark.position = pos;
965  marks.push_back (mark);
966  Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
967  }
968  break;
969  case timecode_show_hours:
970  // Find timecode time of this sample (pos)
971  _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
972  // Go to next whole hour down
973  Timecode::hours_floor( timecode );
974  for (n = 0; n < timecode_nmarks; n++) {
975  _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
976  if ((timecode.hours % timecode_mark_modulo) == 0) {
977  mark.style = ArdourCanvas::Ruler::Mark::Major;
978  snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
979  } else {
980  snprintf (buf, sizeof(buf)," ");
981  mark.style = ArdourCanvas::Ruler::Mark::Micro;
982  }
983  mark.label = buf;
984  mark.position = pos;
985  marks.push_back (mark);
986  Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
987  }
988  break;
989  case timecode_show_many_hours:
990  // Find timecode time of this sample (pos)
991  _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
992  // Go to next whole hour down
993  Timecode::hours_floor (timecode);
994 
995  for (n = 0; n < timecode_nmarks; ) {
996  _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
997  if ((timecode.hours % timecode_mark_modulo) == 0) {
998  mark.style = ArdourCanvas::Ruler::Mark::Major;
999  snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1000  mark.label = buf;
1001  mark.position = pos;
1002  marks.push_back (mark);
1003  ++n;
1004  }
1005  /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
1006  and doing it 1 hour at a time is just stupid (and slow).
1007  */
1008  timecode.hours += timecode_mark_modulo;
1009  }
1010  break;
1011  }
1012 }
1013 
1014 void
1016  ARDOUR::TempoMap::BBTPointList::const_iterator begin,
1017  ARDOUR::TempoMap::BBTPointList::const_iterator end)
1018 {
1019  if (_session == 0) {
1020  return;
1021  }
1022 
1023  TempoMap::BBTPointList::const_iterator i;
1024  Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1025 
1026  _session->bbt_time (lower, lower_beat);
1027  _session->bbt_time (upper, upper_beat);
1028  uint32_t beats = 0;
1029 
1030  bbt_accent_modulo = 1;
1031  bbt_bar_helper_on = false;
1032  bbt_bars = 0;
1033  bbt_nmarks = 1;
1034 
1035  bbt_ruler_scale = bbt_show_many;
1036 
1037  switch (_snap_type) {
1038  case SnapToBeatDiv2:
1039  bbt_beat_subdivision = 2;
1040  break;
1041  case SnapToBeatDiv3:
1042  bbt_beat_subdivision = 3;
1043  break;
1044  case SnapToBeatDiv4:
1045  bbt_beat_subdivision = 4;
1046  break;
1047  case SnapToBeatDiv5:
1048  bbt_beat_subdivision = 5;
1049  bbt_accent_modulo = 2; // XXX YIKES
1050  break;
1051  case SnapToBeatDiv6:
1052  bbt_beat_subdivision = 6;
1053  bbt_accent_modulo = 2; // XXX YIKES
1054  break;
1055  case SnapToBeatDiv7:
1056  bbt_beat_subdivision = 7;
1057  bbt_accent_modulo = 2; // XXX YIKES
1058  break;
1059  case SnapToBeatDiv8:
1060  bbt_beat_subdivision = 8;
1061  bbt_accent_modulo = 2;
1062  break;
1063  case SnapToBeatDiv10:
1064  bbt_beat_subdivision = 10;
1065  bbt_accent_modulo = 2; // XXX YIKES
1066  break;
1067  case SnapToBeatDiv12:
1068  bbt_beat_subdivision = 12;
1069  bbt_accent_modulo = 3;
1070  break;
1071  case SnapToBeatDiv14:
1072  bbt_beat_subdivision = 14;
1073  bbt_accent_modulo = 3; // XXX YIKES!
1074  break;
1075  case SnapToBeatDiv16:
1076  bbt_beat_subdivision = 16;
1077  bbt_accent_modulo = 4;
1078  break;
1079  case SnapToBeatDiv20:
1080  bbt_beat_subdivision = 20;
1081  bbt_accent_modulo = 5;
1082  break;
1083  case SnapToBeatDiv24:
1084  bbt_beat_subdivision = 24;
1085  bbt_accent_modulo = 6;
1086  break;
1087  case SnapToBeatDiv28:
1088  bbt_beat_subdivision = 28;
1089  bbt_accent_modulo = 7;
1090  break;
1091  case SnapToBeatDiv32:
1092  bbt_beat_subdivision = 32;
1093  bbt_accent_modulo = 8;
1094  break;
1095  case SnapToBeatDiv64:
1096  bbt_beat_subdivision = 64;
1097  bbt_accent_modulo = 8;
1098  break;
1099  case SnapToBeatDiv128:
1100  bbt_beat_subdivision = 128;
1101  bbt_accent_modulo = 8;
1102  break;
1103  default:
1104  bbt_beat_subdivision = 4;
1105  break;
1106  }
1107 
1108  if (distance (begin, end) == 0) {
1109  return;
1110  }
1111 
1112  i = end;
1113  i--;
1114  if ((*i).beat >= (*begin).beat) {
1115  bbt_bars = (*i).bar - (*begin).bar;
1116  } else {
1117  bbt_bars = (*i).bar - (*begin).bar - 1;
1118  }
1119  beats = distance (begin, end) - bbt_bars;
1120 
1121  /* Only show the bar helper if there aren't many bars on the screen */
1122  if ((bbt_bars < 2) || (beats < 5)) {
1123  bbt_bar_helper_on = true;
1124  }
1125 
1126  if (bbt_bars > 8192) {
1127  bbt_ruler_scale = bbt_show_many;
1128  } else if (bbt_bars > 1024) {
1129  bbt_ruler_scale = bbt_show_64;
1130  } else if (bbt_bars > 256) {
1131  bbt_ruler_scale = bbt_show_16;
1132  } else if (bbt_bars > 64) {
1133  bbt_ruler_scale = bbt_show_4;
1134  } else if (bbt_bars > 10) {
1135  bbt_ruler_scale = bbt_show_1;
1136  } else if (bbt_bars > 2) {
1137  bbt_ruler_scale = bbt_show_beats;
1138  } else if (bbt_bars > 0) {
1139  bbt_ruler_scale = bbt_show_ticks;
1140  } else {
1141  bbt_ruler_scale = bbt_show_ticks_detail;
1142  }
1143 
1144  if ((bbt_ruler_scale == bbt_show_ticks_detail) && (lower_beat.beats == upper_beat.beats) && (upper_beat.ticks - lower_beat.ticks <= Timecode::BBT_Time::ticks_per_beat / 4)) {
1145  bbt_ruler_scale = bbt_show_ticks_super_detail;
1146  }
1147 }
1148 
1149 static void
1150 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1151 {
1152  ArdourCanvas::Ruler::Mark copy = marks.back();
1153  copy.label = newlabel;
1154  marks.pop_back ();
1155  marks.push_back (copy);
1156 }
1157 
1158 void
1159 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1160 {
1161  if (_session == 0) {
1162  return;
1163  }
1164 
1165  TempoMap::BBTPointList::const_iterator i;
1166 
1167  char buf[64];
1168  gint n = 0;
1169  framepos_t pos;
1170  Timecode::BBT_Time next_beat;
1171  framepos_t next_beat_pos;
1172  uint32_t beats = 0;
1173  uint32_t tick = 0;
1174  uint32_t skip;
1175  uint32_t t;
1176  framepos_t frame_skip;
1177  double frame_skip_error;
1178  double bbt_position_of_helper;
1179  double accumulated_error;
1180  bool i_am_accented = false;
1181  bool helper_active = false;
1182  ArdourCanvas::Ruler::Mark mark;
1183 
1184  ARDOUR::TempoMap::BBTPointList::const_iterator begin;
1185  ARDOUR::TempoMap::BBTPointList::const_iterator end;
1186 
1187  compute_current_bbt_points (lower, upper, begin, end);
1188 
1189  if (distance (begin, end) == 0) {
1190  return;
1191  }
1192 
1193  switch (bbt_ruler_scale) {
1194 
1195  case bbt_show_beats:
1196  beats = distance (begin, end);
1197  bbt_nmarks = beats + 2;
1198 
1199  mark.label = "";
1200  mark.position = lower;
1201  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1202  marks.push_back (mark);
1203 
1204  for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1205 
1206  if ((*i).frame < lower && (bbt_bar_helper_on)) {
1207  snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1208  edit_last_mark_label (marks, buf);
1209  helper_active = true;
1210  } else {
1211 
1212  if ((*i).is_bar()) {
1213  mark.style = ArdourCanvas::Ruler::Mark::Major;
1214  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1215  } else if (((*i).beat % 2 == 1)) {
1216  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1217  buf[0] = '\0';
1218  } else {
1219  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1220  buf[0] = '\0';
1221  }
1222  mark.label = buf;
1223  mark.position = (*i).frame;
1224  marks.push_back (mark);
1225  n++;
1226  }
1227  }
1228  break;
1229 
1230  case bbt_show_ticks:
1231 
1232  beats = distance (begin, end);
1233  bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1234 
1235  bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1236 
1237  // could do marks.assign() here to preallocate
1238 
1239  mark.label = "";
1240  mark.position = lower;
1241  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1242  marks.push_back (mark);
1243 
1244  for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1245 
1246  if ((*i).frame < lower && (bbt_bar_helper_on)) {
1247  snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1248  edit_last_mark_label (marks, buf);
1249  helper_active = true;
1250  } else {
1251 
1252  if ((*i).is_bar()) {
1253  mark.style = ArdourCanvas::Ruler::Mark::Major;
1254  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1255  } else {
1256  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1257  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1258  }
1259  if (((*i).frame < bbt_position_of_helper) && helper_active) {
1260  buf[0] = '\0';
1261  }
1262  mark.label = buf;
1263  mark.position = (*i).frame;
1264  marks.push_back (mark);
1265  n++;
1266  }
1267 
1268  /* Add the tick marks */
1269 
1270  /* Find the next beat */
1271  next_beat.beats = (*i).beat;
1272  next_beat.bars = (*i).bar;
1273  next_beat.ticks = 0;
1274 
1275  if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1276  next_beat.beats += 1;
1277  } else {
1278  next_beat.bars += 1;
1279  next_beat.beats = 1;
1280  }
1281 
1282  next_beat_pos = _session->tempo_map().frame_time(next_beat);
1283 
1284  frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1285  frame_skip_error -= frame_skip;
1286  skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1287 
1288  pos = (*i).frame + frame_skip;
1289  accumulated_error = frame_skip_error;
1290 
1291  tick = skip;
1292 
1293  for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1294 
1295  if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1296  i_am_accented = true;
1297  }
1298 
1299  mark.label = "";
1300 
1301  /* Error compensation for float to framepos_t*/
1302  accumulated_error += frame_skip_error;
1303  if (accumulated_error > 1) {
1304  pos += 1;
1305  accumulated_error -= 1.0f;
1306  }
1307 
1308  mark.position = pos;
1309 
1310  if ((bbt_beat_subdivision > 4) && i_am_accented) {
1311  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1312  } else {
1313  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1314  }
1315  i_am_accented = false;
1316  marks.push_back (mark);
1317  n++;
1318  }
1319  }
1320 
1321  break;
1322 
1323  case bbt_show_ticks_detail:
1324 
1325  beats = distance (begin, end);
1326  bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1327 
1328  bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1329 
1330  mark.label = "";
1331  mark.position = lower;
1332  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1333  marks.push_back (mark);
1334 
1335  for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1336 
1337  if ((*i).frame < lower && (bbt_bar_helper_on)) {
1338  snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1339  edit_last_mark_label (marks, buf);
1340  helper_active = true;
1341  } else {
1342 
1343  if ((*i).is_bar()) {
1344  mark.style = ArdourCanvas::Ruler::Mark::Major;
1345  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1346  } else {
1347  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1348  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1349  }
1350  if (((*i).frame < bbt_position_of_helper) && helper_active) {
1351  buf[0] = '\0';
1352  }
1353  mark.label = buf;
1354  mark.position = (*i).frame;
1355  marks.push_back (mark);
1356  n++;
1357  }
1358 
1359  /* Add the tick marks */
1360 
1361  /* Find the next beat */
1362 
1363  next_beat.beats = (*i).beat;
1364  next_beat.bars = (*i).bar;
1365 
1366  if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1367  next_beat.beats += 1;
1368  } else {
1369  next_beat.bars += 1;
1370  next_beat.beats = 1;
1371  }
1372 
1373  next_beat_pos = _session->tempo_map().frame_time(next_beat);
1374 
1375  frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1376  frame_skip_error -= frame_skip;
1377  skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1378 
1379  pos = (*i).frame + frame_skip;
1380  accumulated_error = frame_skip_error;
1381 
1382  tick = skip;
1383 
1384  for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1385 
1386  if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1387  i_am_accented = true;
1388  }
1389 
1390  if (i_am_accented && (pos > bbt_position_of_helper)){
1391  snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1392  } else {
1393  buf[0] = '\0';
1394  }
1395 
1396  mark.label = buf;
1397 
1398  /* Error compensation for float to framepos_t*/
1399  accumulated_error += frame_skip_error;
1400  if (accumulated_error > 1) {
1401  pos += 1;
1402  accumulated_error -= 1.0f;
1403  }
1404 
1405  mark.position = pos;
1406 
1407  if ((bbt_beat_subdivision > 4) && i_am_accented) {
1408  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1409  } else {
1410  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1411  }
1412  i_am_accented = false;
1413  n++;
1414  }
1415  }
1416 
1417  break;
1418 
1419  case bbt_show_ticks_super_detail:
1420 
1421  beats = distance (begin, end);
1422  bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1423 
1424  bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1425 
1426  mark.label = "";
1427  mark.position = lower;
1428  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1429  marks.push_back (mark);
1430 
1431  for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1432 
1433  if ((*i).frame < lower && (bbt_bar_helper_on)) {
1434  snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1435  edit_last_mark_label (marks, buf);
1436  helper_active = true;
1437  } else {
1438 
1439  if ((*i).is_bar()) {
1440  mark.style = ArdourCanvas::Ruler::Mark::Major;
1441  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1442  } else {
1443  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1444  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1445  }
1446  if (((*i).frame < bbt_position_of_helper) && helper_active) {
1447  buf[0] = '\0';
1448  }
1449  mark.label = buf;
1450  mark.position = (*i).frame;
1451  marks.push_back (mark);
1452  n++;
1453  }
1454 
1455  /* Add the tick marks */
1456 
1457  /* Find the next beat */
1458 
1459  next_beat.beats = (*i).beat;
1460  next_beat.bars = (*i).bar;
1461 
1462  if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1463  next_beat.beats += 1;
1464  } else {
1465  next_beat.bars += 1;
1466  next_beat.beats = 1;
1467  }
1468 
1469  next_beat_pos = _session->tempo_map().frame_time(next_beat);
1470 
1471  frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1472  frame_skip_error -= frame_skip;
1473  skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1474 
1475  pos = (*i).frame + frame_skip;
1476  accumulated_error = frame_skip_error;
1477 
1478  tick = skip;
1479 
1480  for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1481 
1482  if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1483  i_am_accented = true;
1484  }
1485 
1486  if (pos > bbt_position_of_helper) {
1487  snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1488  } else {
1489  buf[0] = '\0';
1490  }
1491 
1492  mark.label = buf;
1493 
1494  /* Error compensation for float to framepos_t*/
1495  accumulated_error += frame_skip_error;
1496  if (accumulated_error > 1) {
1497  pos += 1;
1498  accumulated_error -= 1.0f;
1499  }
1500 
1501  mark.position = pos;
1502 
1503  if ((bbt_beat_subdivision > 4) && i_am_accented) {
1504  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1505  } else {
1506  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1507  }
1508  i_am_accented = false;
1509  marks.push_back (mark);
1510  n++;
1511  }
1512  }
1513 
1514  break;
1515 
1516  case bbt_show_many:
1517  bbt_nmarks = 1;
1518  snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1519  mark.style = ArdourCanvas::Ruler::Mark::Major;
1520  mark.label = buf;
1521  mark.position = lower;
1522  marks.push_back (mark);
1523  break;
1524 
1525  case bbt_show_64:
1526  bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1527  for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1528  if ((*i).is_bar()) {
1529  if ((*i).bar % 64 == 1) {
1530  if ((*i).bar % 256 == 1) {
1531  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1532  mark.style = ArdourCanvas::Ruler::Mark::Major;
1533  } else {
1534  buf[0] = '\0';
1535  if ((*i).bar % 256 == 129) {
1536  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1537  } else {
1538  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1539  }
1540  }
1541  mark.label = buf;
1542  mark.position = (*i).frame;
1543  marks.push_back (mark);
1544  ++n;
1545  }
1546  }
1547  }
1548  break;
1549 
1550  case bbt_show_16:
1551  bbt_nmarks = (bbt_bars / 16) + 1;
1552  for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1553  if ((*i).is_bar()) {
1554  if ((*i).bar % 16 == 1) {
1555  if ((*i).bar % 64 == 1) {
1556  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1557  mark.style = ArdourCanvas::Ruler::Mark::Major;
1558  } else {
1559  buf[0] = '\0';
1560  if ((*i).bar % 64 == 33) {
1561  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1562  } else {
1563  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1564  }
1565  }
1566  mark.label = buf;
1567  mark.position = (*i).frame;
1568  marks.push_back (mark);
1569  ++n;
1570  }
1571  }
1572  }
1573  break;
1574 
1575  case bbt_show_4:
1576  bbt_nmarks = (bbt_bars / 4) + 1;
1577  for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1578  if ((*i).is_bar()) {
1579  if ((*i).bar % 4 == 1) {
1580  if ((*i).bar % 16 == 1) {
1581  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1582  mark.style = ArdourCanvas::Ruler::Mark::Major;
1583  } else {
1584  buf[0] = '\0';
1585  if ((*i).bar % 16 == 9) {
1586  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1587  } else {
1588  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1589  }
1590  }
1591  mark.label = buf;
1592  mark.position = (*i).frame;
1593  marks.push_back (mark);
1594  ++n;
1595  }
1596  }
1597  }
1598  break;
1599 
1600  case bbt_show_1:
1601 // default:
1602  bbt_nmarks = bbt_bars + 2;
1603  for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1604  if ((*i).is_bar()) {
1605  if ((*i).bar % 4 == 1) {
1606  snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1607  mark.style = ArdourCanvas::Ruler::Mark::Major;
1608  } else {
1609  buf[0] = '\0';
1610  if ((*i).bar % 4 == 3) {
1611  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1612  } else {
1613  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1614  }
1615  }
1616  mark.label = buf;
1617  mark.position = (*i).frame;
1618  marks.push_back (mark);
1619  ++n;
1620  }
1621  }
1622  break;
1623 
1624  }
1625 }
1626 
1627 void
1629 {
1630  _samples_ruler_interval = (upper - lower) / 5;
1631 }
1632 
1633 void
1634 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1635 {
1636  framepos_t pos;
1637  framepos_t const ilower = (framepos_t) floor (lower);
1638  gchar buf[16];
1639  gint nmarks;
1640  gint n;
1641  ArdourCanvas::Ruler::Mark mark;
1642 
1643  if (_session == 0) {
1644  return;
1645  }
1646 
1647  nmarks = 5;
1648  for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1649  snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1650  mark.label = buf;
1651  mark.position = pos;
1652  mark.style = ArdourCanvas::Ruler::Mark::Major;
1653  marks.push_back (mark);
1654  }
1655 }
1656 
1657 static void
1659  framepos_t sample_rate,
1660  long *hrs_p,
1661  long *mins_p,
1662  long *secs_p,
1663  long *millisecs_p)
1664 
1665 {
1666  framepos_t left;
1667  long hrs;
1668  long mins;
1669  long secs;
1670  long millisecs;
1671 
1672  left = sample;
1673  hrs = left / (sample_rate * 60 * 60 * 1000);
1674  left -= hrs * sample_rate * 60 * 60 * 1000;
1675  mins = left / (sample_rate * 60 * 1000);
1676  left -= mins * sample_rate * 60 * 1000;
1677  secs = left / (sample_rate * 1000);
1678  left -= secs * sample_rate * 1000;
1679  millisecs = left / sample_rate;
1680 
1681  *millisecs_p = millisecs;
1682  *secs_p = secs;
1683  *mins_p = mins;
1684  *hrs_p = hrs;
1685 
1686  return;
1687 }
1688 
1689 void
1691 {
1692  framepos_t fr = _session->frame_rate() * 1000;
1693  framepos_t spacer;
1694 
1695  if (_session == 0) {
1696  return;
1697  }
1698 
1699 
1700  /* to prevent 'flashing' */
1701  if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1702  lower -= spacer;
1703  } else {
1704  lower = 0;
1705  }
1706  upper += spacer;
1707  framecnt_t const range = (upper - lower) * 1000;
1708 
1709  if (range <= (fr / 10)) { /* 0-0.1 second */
1710  minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1711  minsec_ruler_scale = minsec_show_msecs;
1712  minsec_mark_modulo = 10;
1713  minsec_nmarks = 2 + (range / minsec_mark_interval);
1714  } else if (range <= (fr / 2)) { /* 0-0.5 second */
1715  minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1716  minsec_ruler_scale = minsec_show_msecs;
1717  minsec_mark_modulo = 100;
1718  minsec_nmarks = 2 + (range / minsec_mark_interval);
1719  } else if (range <= fr) { /* 0-1 second */
1720  minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1721  minsec_ruler_scale = minsec_show_msecs;
1722  minsec_mark_modulo = 200;
1723  minsec_nmarks = 2 + (range / minsec_mark_interval);
1724  } else if (range <= 2 * fr) { /* 1-2 seconds */
1725  minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1726  minsec_ruler_scale = minsec_show_msecs;
1727  minsec_mark_modulo = 500;
1728  minsec_nmarks = 2 + (range / minsec_mark_interval);
1729  } else if (range <= 8 * fr) { /* 2-5 seconds */
1730  minsec_mark_interval = fr / 5; /* show 2 seconds */
1731  minsec_ruler_scale = minsec_show_msecs;
1732  minsec_mark_modulo = 1000;
1733  minsec_nmarks = 2 + (range / minsec_mark_interval);
1734  } else if (range <= 16 * fr) { /* 8-16 seconds */
1735  minsec_mark_interval = fr; /* show 1 seconds */
1736  minsec_ruler_scale = minsec_show_seconds;
1737  minsec_mark_modulo = 2;
1738  minsec_nmarks = 2 + (range / minsec_mark_interval);
1739  } else if (range <= 30 * fr) { /* 10-30 seconds */
1740  minsec_mark_interval = fr; /* show 1 seconds */
1741  minsec_ruler_scale = minsec_show_seconds;
1742  minsec_mark_modulo = 5;
1743  minsec_nmarks = 2 + (range / minsec_mark_interval);
1744  } else if (range <= 60 * fr) { /* 30-60 seconds */
1745  minsec_mark_interval = fr; /* show 1 seconds */
1746  minsec_ruler_scale = minsec_show_seconds;
1747  minsec_mark_modulo = 5;
1748  minsec_nmarks = 2 + (range / minsec_mark_interval);
1749  } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1750  minsec_mark_interval = 5 * fr; /* show 5 seconds */
1751  minsec_ruler_scale = minsec_show_seconds;
1752  minsec_mark_modulo = 3;
1753  minsec_nmarks = 2 + (range / minsec_mark_interval);
1754  } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1755  minsec_mark_interval = 5 * fr; /* show 10 seconds */
1756  minsec_ruler_scale = minsec_show_seconds;
1757  minsec_mark_modulo = 30;
1758  minsec_nmarks = 2 + (range / minsec_mark_interval);
1759  } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1760  minsec_mark_interval = 30 * fr; /* show 30 seconds */
1761  minsec_ruler_scale = minsec_show_seconds;
1762  minsec_mark_modulo = 120;
1763  minsec_nmarks = 2 + (range / minsec_mark_interval);
1764  } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1765  minsec_mark_interval = 60 * fr; /* show 1 minute */
1766  minsec_ruler_scale = minsec_show_minutes;
1767  minsec_mark_modulo = 5;
1768  minsec_nmarks = 2 + (range / minsec_mark_interval);
1769  } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1770  minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1771  minsec_ruler_scale = minsec_show_minutes;
1772  minsec_mark_modulo = 10;
1773  minsec_nmarks = 2 + (range / minsec_mark_interval);
1774  } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1775  minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1776  minsec_ruler_scale = minsec_show_minutes;
1777  minsec_mark_modulo = 30;
1778  minsec_nmarks = 2 + (range / minsec_mark_interval);
1779  } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1780  minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1781  minsec_ruler_scale = minsec_show_minutes;
1782  minsec_mark_modulo = 60;
1783  minsec_nmarks = 2 + (range / minsec_mark_interval);
1784  } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1785  minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1786  minsec_ruler_scale = minsec_show_hours;
1787  minsec_mark_modulo = 2;
1788  minsec_nmarks = 2 + (range / minsec_mark_interval);
1789  } else {
1790 
1791  const framecnt_t hours_in_range = range / (60 * 60 * fr);
1792  const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1793 
1794  /* Normally we do not need to know anything about the width of the canvas
1795  to set the ruler scale, because the caller has already determined
1796  the width and set lower + upper arguments to this function to match that.
1797 
1798  But in this case, where the range defined by lower and uppper can vary
1799  substantially (anything from 24hrs+ to several billion years)
1800  trying to decide which tick marks to show does require us to know
1801  about the available width.
1802  */
1803 
1804  minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1805  minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1806  minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1807  minsec_ruler_scale = minsec_show_many_hours;
1808  }
1809 }
1810 
1811 void
1812 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1813 {
1814  framepos_t pos;
1815  framepos_t spacer;
1816  long hrs, mins, secs, millisecs;
1817  gchar buf[16];
1818  gint n;
1819  ArdourCanvas::Ruler::Mark mark;
1820 
1821  if (_session == 0) {
1822  return;
1823  }
1824 
1825  /* to prevent 'flashing' */
1826  if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1827  lower = lower - spacer;
1828  } else {
1829  lower = 0;
1830  }
1831 
1832  pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1833 
1834  switch (minsec_ruler_scale) {
1835 
1836  case minsec_show_msecs:
1837  for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1838  sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1839  if (millisecs % minsec_mark_modulo == 0) {
1840  if (millisecs == 0) {
1841  mark.style = ArdourCanvas::Ruler::Mark::Major;
1842  } else {
1843  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1844  }
1845  snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1846  } else {
1847  buf[0] = '\0';
1848  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1849  }
1850  mark.label = buf;
1851  mark.position = pos/1000.0;
1852  marks.push_back (mark);
1853  }
1854  break;
1855 
1856  case minsec_show_seconds:
1857  for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1858  sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1859  if (secs % minsec_mark_modulo == 0) {
1860  if (secs == 0) {
1861  mark.style = ArdourCanvas::Ruler::Mark::Major;
1862  } else {
1863  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1864  }
1865  snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1866  } else {
1867  buf[0] = '\0';
1868  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1869  }
1870  mark.label = buf;
1871  mark.position = pos/1000.0;
1872  marks.push_back (mark);
1873  }
1874  break;
1875 
1876  case minsec_show_minutes:
1877  for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1878  sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1879  if (mins % minsec_mark_modulo == 0) {
1880  if (mins == 0) {
1881  mark.style = ArdourCanvas::Ruler::Mark::Major;
1882  } else {
1883  mark.style = ArdourCanvas::Ruler::Mark::Minor;
1884  }
1885  snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1886  } else {
1887  buf[0] = '\0';
1888  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1889  }
1890  mark.label = buf;
1891  mark.position = pos/1000.0;
1892  marks.push_back (mark);
1893  }
1894  break;
1895 
1896  case minsec_show_hours:
1897  for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1898  sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1899  if (hrs % minsec_mark_modulo == 0) {
1900  mark.style = ArdourCanvas::Ruler::Mark::Major;
1901  snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1902  } else {
1903  buf[0] = '\0';
1904  mark.style = ArdourCanvas::Ruler::Mark::Micro;
1905  }
1906  mark.label = buf;
1907  mark.position = pos/1000.0;
1908  marks.push_back (mark);
1909  }
1910  break;
1911 
1912  case minsec_show_many_hours:
1913  for (n = 0; n < minsec_nmarks; ) {
1914  sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1915  if (hrs % minsec_mark_modulo == 0) {
1916  mark.style = ArdourCanvas::Ruler::Mark::Major;
1917  snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1918  mark.label = buf;
1919  mark.position = pos/1000.0;
1920  marks.push_back (mark);
1921  ++n;
1922  }
1923  pos += minsec_mark_interval;
1924  }
1925  break;
1926  }
1927 }
void metric_get_bbt(std::vector< ArdourCanvas::Ruler::Mark > &, gdouble, gdouble, gint)
void initialize_rulers()
void clear_markers()
Definition: editor_ops.cc:2319
bool get_sae() const
Definition: profile.h:48
bool canvas_ruler_event(GdkEvent *event, ArdourCanvas::Item *, ItemType)
const std::string & value() const
Definition: xml++.h:159
Editor * _editor
std::list< Location * > LocationList
Definition: location.h:167
ItemType
Definition: editor_items.h:23
void toggle_video_timeline_locked()
void mouse_add_new_tempo_event(framepos_t where)
Definition: ardour_ui.h:130
static ARDOUR_UI * instance()
Definition: ardour_ui.h:187
void compute_fixed_ruler_scale()
MinsecMetric(Editor *e)
void mouse_add_new_marker(framepos_t where, bool is_cd=false)
SamplesMetric(Editor *e)
Definition: Beats.hpp:239
void store_ruler_visibility()
Editor * _editor
void get_marks(std::vector< ArdourCanvas::Ruler::Mark > &marks, double lower, double upper, int maxchars) const
#define ENSURE_GUI_THREAD(obj, method,...)
Definition: gui_thread.h:34
void get_marks(std::vector< ArdourCanvas::Ruler::Mark > &marks, double lower, double upper, int maxchars) const
TimecodeMetric(Editor *e)
void set_minsec_ruler_scale(framepos_t, framepos_t)
void metric_get_samples(std::vector< ArdourCanvas::Ruler::Mark > &, gdouble, gdouble, gint)
#define _(Text)
Definition: i18n.h:11
void mouse_add_new_meter_event(framepos_t where)
void get_marks(std::vector< ArdourCanvas::Ruler::Mark > &marks, double lower, double upper, int maxchars) const
static ArdourCanvas::Ruler::Metric * _samples_metric
#define X_(Text)
Definition: i18n.h:13
void popup_ruler_menu(framepos_t where=0, ItemType type=RegionItem)
void mouse_add_new_punch(framepos_t)
int64_t framecnt_t
Definition: types.h:76
XMLProperty * property(const char *)
Definition: xml++.cc:413
void metric_get_minsec(std::vector< ArdourCanvas::Ruler::Mark > &, gdouble, gdouble, gint)
void clear_ranges()
Definition: editor_ops.cc:2334
VideoTimeLine * video_timeline
Definition: ardour_ui.h:236
void get_marks(std::vector< ArdourCanvas::Ruler::Mark > &marks, double lower, double upper, int maxchars) const
Editor * _editor
void set_timecode_ruler_scale(framepos_t, framepos_t)
void mouse_add_new_range(framepos_t)
bool string_is_affirmative(const std::string &str)
Definition: convert.cc:282
Definition: amp.h:29
framecnt_t get_current_zoom() const
Definition: editor.h:290
int64_t framepos_t
Definition: types.h:66
Editor * _editor
LIBARDOUR_API RuntimeProfile * Profile
Definition: globals.cc:120
void unhide_markers()
Definition: editor_ops.cc:2364
void update_fixed_rulers()
void compute_bbt_ruler_scale(framepos_t lower, framepos_t upper, ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_begin, ARDOUR::TempoMap::BBTPointList::const_iterator current_bbt_points_end)
XMLProperty * add_property(const char *name, const std::string &value)
void update_tempo_based_rulers(ARDOUR::TempoMap::BBTPointList::const_iterator &begin, ARDOUR::TempoMap::BBTPointList::const_iterator &end)
#define upper
Definition: auto_spin.cc:28
static void edit_last_mark_label(std::vector< ArdourCanvas::Ruler::Mark > &marks, const std::string &newlabel)
void unhide_ranges()
Definition: editor_ops.cc:2375
Definition: editor.h:134
void toggle_xjadeo_proc(int state=-1)
static ArdourCanvas::Ruler::Metric * _bbt_metric
Definition: xml++.h:95
static UIConfiguration * config()
Definition: ardour_ui.h:188
void metric_get_timecode(std::vector< ArdourCanvas::Ruler::Mark > &, gdouble, gdouble, gint)
Definition: debug.h:30
void update_ruler_visibility()
#define lower
Definition: auto_spin.cc:29
BBTMetric(Editor *e)
bool ruler_label_button_release(GdkEventButton *)
static bool is_context_menu_event(GdkEventButton *)
Definition: keyboard.cc:506
static void sample_to_clock_parts(framepos_t sample, framepos_t sample_rate, long *hrs_p, long *mins_p, long *secs_p, long *millisecs_p)
static ArdourCanvas::Ruler::Metric * _minsec_metric
void restore_ruler_visibility()
bool found_xjadeo()
void set_samples_ruler_scale(framepos_t, framepos_t)
static ArdourCanvas::Ruler::Metric * _timecode_metric
void set_video_timeline_height(const int)
void mouse_add_new_loop(framepos_t)
void update_just_timecode()