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