2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
24 #include <cstdio> // for sprintf, grrr
30 #include <gtk/gtkaction.h>
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"
38 #include "ardour/session.h"
39 #include "ardour/tempo.h"
40 #include "ardour/profile.h"
42 #include "gtkmm2ext/gtk_ui.h"
43 #include "gtkmm2ext/keyboard.h"
45 #include "ardour_ui.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 #include "ui_config.h"
58 using namespace ARDOUR;
61 using namespace Editing;
63 /* the order here must match the "metric" enums in editor.h */
65 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
68 TimecodeMetric (Editor* e) : _editor (e) {}
70 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
71 _editor->metric_get_timecode (marks, lower, upper, maxchars);
78 class SamplesMetric : public ArdourCanvas::Ruler::Metric
81 SamplesMetric (Editor* e) : _editor (e) {}
83 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
84 _editor->metric_get_samples (marks, lower, upper, maxchars);
91 class BBTMetric : public ArdourCanvas::Ruler::Metric
94 BBTMetric (Editor* e) : _editor (e) {}
96 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
97 _editor->metric_get_bbt (marks, lower, upper, maxchars);
104 class MinsecMetric : public ArdourCanvas::Ruler::Metric
107 MinsecMetric (Editor* e) : _editor (e) {}
109 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
110 _editor->metric_get_minsec (marks, lower, upper, maxchars);
117 static ArdourCanvas::Ruler::Metric* _bbt_metric;
118 static ArdourCanvas::Ruler::Metric* _timecode_metric;
119 static ArdourCanvas::Ruler::Metric* _samples_metric;
120 static ArdourCanvas::Ruler::Metric* _minsec_metric;
123 Editor::initialize_rulers ()
125 ruler_grabbed_widget = 0;
127 Pango::FontDescription font (UIConfiguration::instance().get_SmallerFont());
129 _timecode_metric = new TimecodeMetric (this);
130 _bbt_metric = new BBTMetric (this);
131 _minsec_metric = new MinsecMetric (this);
132 _samples_metric = new SamplesMetric (this);
134 timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
135 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
136 timecode_ruler->set_font_description (font);
137 CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
140 samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
141 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
142 samples_ruler->set_font_description (font);
143 CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
145 minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
146 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
147 minsec_ruler->set_font_description (font);
148 CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
151 bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
152 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
153 bbt_ruler->set_font_description (font);
154 CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
157 using namespace Box_Helpers;
158 BoxList & lab_children = time_bars_vbox.children();
160 lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
161 lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
162 lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
163 lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
164 lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
165 lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
166 lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
167 lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
168 lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
169 lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
170 lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
172 /* 1 event handler to bind them all ... */
174 timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
175 minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
176 bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
177 samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
179 visible_timebars = 0; /*this will be changed below */
183 Editor::ruler_label_button_release (GdkEventButton* ev)
185 if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
187 ruler_dialog = new RulerDialog ();
189 ruler_dialog->present ();
196 Editor::popup_ruler_menu (framepos_t where, ItemType t)
198 using namespace Menu_Helpers;
200 if (editor_ruler_menu == 0) {
201 editor_ruler_menu = new Menu;
202 editor_ruler_menu->set_name ("ArdourContextMenu");
205 // always build from scratch
206 MenuList& ruler_items = editor_ruler_menu->items();
207 editor_ruler_menu->set_name ("ArdourContextMenu");
212 ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false)));
213 ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
214 ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
217 case RangeMarkerBarItem:
218 ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
219 ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
220 ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
223 case TransportMarkerBarItem:
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)));
228 case CdMarkerBarItem:
230 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true)));
234 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
238 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
242 /* proper headings would be nice
243 * but AFAICT the only way to get them will be to define a
244 * special GTK style for insensitive Elements or subclass MenuItem
246 //ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
247 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
248 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
249 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
250 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
251 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
252 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
253 if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
255 ruler_items.push_back (SeparatorElem ());
257 //ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
258 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
259 ruler_items.push_back (CheckMenuElem (_("Lock")));
261 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
262 vtl_lock->set_active(is_video_timeline_locked());
263 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
266 ruler_items.push_back (SeparatorElem ());
268 //ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
269 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
270 ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
272 Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
273 if (!ARDOUR_UI::instance()->video_timeline->found_xjadeo()) {
274 xjadeo_toggle->set_sensitive(false);
276 xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
277 xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
285 if (!ruler_items.empty()) {
286 editor_ruler_menu->popup (1, gtk_get_current_event_time());
289 no_ruler_shown_update = false;
293 Editor::store_ruler_visibility ()
295 XMLNode* node = new XMLNode(X_("RulerVisibility"));
297 node->add_property (X_("timecode"), ruler_timecode_action->get_active() ? "yes": "no");
298 node->add_property (X_("bbt"), ruler_bbt_action->get_active() ? "yes": "no");
299 node->add_property (X_("samples"), ruler_samples_action->get_active() ? "yes": "no");
300 node->add_property (X_("minsec"), ruler_minsec_action->get_active() ? "yes": "no");
301 node->add_property (X_("tempo"), ruler_tempo_action->get_active() ? "yes": "no");
302 node->add_property (X_("meter"), ruler_meter_action->get_active() ? "yes": "no");
303 node->add_property (X_("marker"), ruler_marker_action->get_active() ? "yes": "no");
304 node->add_property (X_("rangemarker"), ruler_range_action->get_active() ? "yes": "no");
305 node->add_property (X_("transportmarker"), ruler_loop_punch_action->get_active() ? "yes": "no");
306 node->add_property (X_("cdmarker"), ruler_cd_marker_action->get_active() ? "yes": "no");
307 node->add_property (X_("videotl"), ruler_video_action->get_active() ? "yes": "no");
309 _session->add_extra_xml (*node);
310 _session->set_dirty ();
314 Editor::restore_ruler_visibility ()
316 XMLProperty const * prop;
317 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
319 no_ruler_shown_update = true;
322 if ((prop = node->property ("timecode")) != 0) {
323 if (string_is_affirmative (prop->value())) {
324 ruler_timecode_action->set_active (true);
326 ruler_timecode_action->set_active (false);
329 if ((prop = node->property ("bbt")) != 0) {
330 if (string_is_affirmative (prop->value())) {
331 ruler_bbt_action->set_active (true);
333 ruler_bbt_action->set_active (false);
336 if ((prop = node->property ("samples")) != 0) {
337 if (string_is_affirmative (prop->value())) {
338 ruler_samples_action->set_active (true);
340 ruler_samples_action->set_active (false);
343 if ((prop = node->property ("minsec")) != 0) {
344 if (string_is_affirmative (prop->value())) {
345 ruler_minsec_action->set_active (true);
347 ruler_minsec_action->set_active (false);
350 if ((prop = node->property ("tempo")) != 0) {
351 if (string_is_affirmative (prop->value())) {
352 ruler_tempo_action->set_active (true);
354 ruler_tempo_action->set_active (false);
357 if ((prop = node->property ("meter")) != 0) {
358 if (string_is_affirmative (prop->value())) {
359 ruler_meter_action->set_active (true);
361 ruler_meter_action->set_active (false);
364 if ((prop = node->property ("marker")) != 0) {
365 if (string_is_affirmative (prop->value())) {
366 ruler_marker_action->set_active (true);
368 ruler_marker_action->set_active (false);
371 if ((prop = node->property ("rangemarker")) != 0) {
372 if (string_is_affirmative (prop->value())) {
373 ruler_range_action->set_active (true);
375 ruler_range_action->set_active (false);
379 if ((prop = node->property ("transportmarker")) != 0) {
380 if (string_is_affirmative (prop->value())) {
381 ruler_loop_punch_action->set_active (true);
383 ruler_loop_punch_action->set_active (false);
387 if ((prop = node->property ("cdmarker")) != 0) {
388 if (string_is_affirmative (prop->value())) {
389 ruler_cd_marker_action->set_active (true);
391 ruler_cd_marker_action->set_active (false);
395 // this _session doesn't yet know about the cdmarker ruler
396 // as a benefit to the user who doesn't know the feature exists, show the ruler if
397 // any cd marks exist
398 ruler_cd_marker_action->set_active (false);
399 const Locations::LocationList & locs = _session->locations()->list();
400 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
401 if ((*i)->is_cd_marker()) {
402 ruler_cd_marker_action->set_active (true);
408 if ((prop = node->property ("videotl")) != 0) {
409 if (string_is_affirmative (prop->value())) {
410 ruler_video_action->set_active (true);
412 ruler_video_action->set_active (false);
418 no_ruler_shown_update = false;
419 update_ruler_visibility ();
423 Editor::update_ruler_visibility ()
425 int visible_timebars = 0;
427 if (no_ruler_shown_update) {
431 /* the order of the timebars is fixed, so we have to go through each one
432 * and adjust its position depending on what is shown.
434 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
435 * loop/punch, cd markers, location markers
443 /* gtk update probs require this (damn) */
446 range_mark_label.hide();
447 transport_mark_label.hide();
448 cd_mark_label.hide();
450 videotl_label.hide();
453 if (ruler_minsec_action->get_active()) {
454 old_unit_pos = minsec_ruler->position().y;
455 if (tbpos != old_unit_pos) {
456 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
458 minsec_ruler->show();
460 tbpos += timebar_height;
461 tbgpos += timebar_height;
464 minsec_ruler->hide();
468 if (ruler_timecode_action->get_active()) {
469 old_unit_pos = timecode_ruler->position().y;
470 if (tbpos != old_unit_pos) {
471 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
473 timecode_ruler->show();
474 timecode_label.show();
475 tbpos += timebar_height;
476 tbgpos += timebar_height;
479 timecode_ruler->hide();
480 timecode_label.hide();
483 if (ruler_samples_action->get_active()) {
484 old_unit_pos = samples_ruler->position().y;
485 if (tbpos != old_unit_pos) {
486 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
488 samples_ruler->show();
489 samples_label.show();
490 tbpos += timebar_height;
491 tbgpos += timebar_height;
494 samples_ruler->hide();
495 samples_label.hide();
498 if (ruler_bbt_action->get_active()) {
499 old_unit_pos = bbt_ruler->position().y;
500 if (tbpos != old_unit_pos) {
501 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
505 tbpos += timebar_height;
506 tbgpos += timebar_height;
513 if (ruler_meter_action->get_active()) {
514 old_unit_pos = meter_group->position().y;
515 if (tbpos != old_unit_pos) {
516 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
520 tbpos += timebar_height;
521 tbgpos += timebar_height;
528 if (ruler_tempo_action->get_active()) {
529 old_unit_pos = tempo_group->position().y;
530 if (tbpos != old_unit_pos) {
531 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
535 tbpos += timebar_height;
536 tbgpos += timebar_height;
543 if (ruler_range_action->get_active()) {
544 old_unit_pos = range_marker_group->position().y;
545 if (tbpos != old_unit_pos) {
546 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
548 range_marker_group->show();
549 range_mark_label.show();
551 tbpos += timebar_height;
552 tbgpos += timebar_height;
555 range_marker_group->hide();
556 range_mark_label.hide();
559 if (ruler_loop_punch_action->get_active()) {
560 old_unit_pos = transport_marker_group->position().y;
561 if (tbpos != old_unit_pos) {
562 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
564 transport_marker_group->show();
565 transport_mark_label.show();
566 tbpos += timebar_height;
567 tbgpos += timebar_height;
570 transport_marker_group->hide();
571 transport_mark_label.hide();
574 if (ruler_cd_marker_action->get_active()) {
575 old_unit_pos = cd_marker_group->position().y;
576 if (tbpos != old_unit_pos) {
577 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
579 cd_marker_group->show();
580 cd_mark_label.show();
581 tbpos += timebar_height;
582 tbgpos += timebar_height;
584 // make sure all cd markers show up in their respective places
585 update_cd_marker_display();
587 cd_marker_group->hide();
588 cd_mark_label.hide();
589 // make sure all cd markers show up in their respective places
590 update_cd_marker_display();
593 if (ruler_marker_action->get_active()) {
594 old_unit_pos = marker_group->position().y;
595 if (tbpos != old_unit_pos) {
596 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
598 marker_group->show();
600 tbpos += timebar_height;
601 tbgpos += timebar_height;
604 marker_group->hide();
608 if (ruler_video_action->get_active()) {
609 old_unit_pos = videotl_group->position().y;
610 if (tbpos != old_unit_pos) {
611 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
613 videotl_group->show();
614 videotl_label.show();
615 tbpos += timebar_height * videotl_bar_height;
616 tbgpos += timebar_height * videotl_bar_height;
617 visible_timebars+=videotl_bar_height;
618 queue_visual_videotimeline_update();
620 videotl_group->hide();
621 videotl_label.hide();
622 update_video_timeline(true);
625 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
627 /* move hv_scroll_group (trackviews) to the end of the timebars
630 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
632 compute_fixed_ruler_scale ();
633 update_fixed_rulers();
634 redisplay_tempo (false);
636 /* Changing ruler visibility means that any lines on markers might need updating */
637 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
638 i->second->setup_lines ();
643 Editor::update_just_timecode ()
645 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
651 framepos_t rightmost_frame = leftmost_frame + current_page_samples();
653 if (ruler_timecode_action->get_active()) {
654 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
659 Editor::compute_fixed_ruler_scale ()
665 if (ruler_timecode_action->get_active()) {
666 set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
669 if (ruler_minsec_action->get_active()) {
670 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
673 if (ruler_samples_action->get_active()) {
674 set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
679 Editor::update_fixed_rulers ()
681 framepos_t rightmost_frame;
687 compute_fixed_ruler_scale ();
689 _timecode_metric->units_per_pixel = samples_per_pixel;
690 _samples_metric->units_per_pixel = samples_per_pixel;
691 _minsec_metric->units_per_pixel = samples_per_pixel;
693 rightmost_frame = leftmost_frame + current_page_samples();
695 /* these force a redraw, which in turn will force execution of the metric callbacks
696 to compute the relevant ticks to display.
699 if (ruler_timecode_action->get_active()) {
700 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
703 if (ruler_samples_action->get_active()) {
704 samples_ruler->set_range (leftmost_frame, rightmost_frame);
707 if (ruler_minsec_action->get_active()) {
708 minsec_ruler->set_range (leftmost_frame, rightmost_frame);
713 Editor::update_tempo_based_rulers (std::vector<TempoMap::BBTPoint>& grid)
719 compute_bbt_ruler_scale (grid, leftmost_frame, leftmost_frame+current_page_samples());
721 _bbt_metric->units_per_pixel = samples_per_pixel;
723 if (ruler_bbt_action->get_active()) {
724 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
730 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
741 fr = _session->frame_rate();
743 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
744 lower = lower - spacer;
749 upper = upper + spacer;
750 framecnt_t const range = upper - lower;
752 if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
753 timecode_ruler_scale = timecode_show_bits;
754 timecode_mark_modulo = 20;
755 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
756 } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
757 timecode_ruler_scale = timecode_show_frames;
758 timecode_mark_modulo = 1;
759 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
760 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
761 timecode_ruler_scale = timecode_show_frames;
762 timecode_mark_modulo = 2;
763 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
764 } else if (range <= fr) { /* 0.5-1 second */
765 timecode_ruler_scale = timecode_show_frames;
766 timecode_mark_modulo = 5;
767 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
768 } else if (range <= 2 * fr) { /* 1-2 seconds */
769 timecode_ruler_scale = timecode_show_frames;
770 timecode_mark_modulo = 10;
771 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
772 } else if (range <= 8 * fr) { /* 2-8 seconds */
773 timecode_ruler_scale = timecode_show_seconds;
774 timecode_mark_modulo = 1;
775 timecode_nmarks = 2 + (range / fr);
776 } else if (range <= 16 * fr) { /* 8-16 seconds */
777 timecode_ruler_scale = timecode_show_seconds;
778 timecode_mark_modulo = 2;
779 timecode_nmarks = 2 + (range / fr);
780 } else if (range <= 30 * fr) { /* 16-30 seconds */
781 timecode_ruler_scale = timecode_show_seconds;
782 timecode_mark_modulo = 5;
783 timecode_nmarks = 2 + (range / fr);
784 } else if (range <= 60 * fr) { /* 30-60 seconds */
785 timecode_ruler_scale = timecode_show_seconds;
786 timecode_mark_modulo = 5;
787 timecode_nmarks = 2 + (range / fr);
788 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
789 timecode_ruler_scale = timecode_show_seconds;
790 timecode_mark_modulo = 15;
791 timecode_nmarks = 2 + (range / fr);
792 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
793 timecode_ruler_scale = timecode_show_seconds;
794 timecode_mark_modulo = 30;
795 timecode_nmarks = 2 + (range / fr);
796 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
797 timecode_ruler_scale = timecode_show_minutes;
798 timecode_mark_modulo = 2;
799 timecode_nmarks = 2 + 10;
800 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
801 timecode_ruler_scale = timecode_show_minutes;
802 timecode_mark_modulo = 5;
803 timecode_nmarks = 2 + 30;
804 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
805 timecode_ruler_scale = timecode_show_minutes;
806 timecode_mark_modulo = 10;
807 timecode_nmarks = 2 + 60;
808 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
809 timecode_ruler_scale = timecode_show_minutes;
810 timecode_mark_modulo = 30;
811 timecode_nmarks = 2 + (60 * 4);
812 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
813 timecode_ruler_scale = timecode_show_hours;
814 timecode_mark_modulo = 1;
815 timecode_nmarks = 2 + 8;
816 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
817 timecode_ruler_scale = timecode_show_hours;
818 timecode_mark_modulo = 1;
819 timecode_nmarks = 2 + 24;
822 const framecnt_t hours_in_range = range / (60 * 60 * fr);
823 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
825 /* Normally we do not need to know anything about the width of the canvas
826 to set the ruler scale, because the caller has already determined
827 the width and set lower + upper arguments to this function to match that.
829 But in this case, where the range defined by lower and uppper can vary
830 substantially (basically anything from 24hrs+ to several billion years)
831 trying to decide which tick marks to show does require us to know
832 about the available width.
835 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
836 timecode_ruler_scale = timecode_show_many_hours;
837 timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
842 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
846 Timecode::Time timecode;
849 ArdourCanvas::Ruler::Mark mark;
855 if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
856 lower = lower - spacer;
861 pos = (framecnt_t) floor (lower);
863 switch (timecode_ruler_scale) {
864 case timecode_show_bits:
865 // Find timecode time of this sample (pos) with subframe accuracy
866 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
867 for (n = 0; n < timecode_nmarks; n++) {
868 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
869 if ((timecode.subframes % timecode_mark_modulo) == 0) {
870 if (timecode.subframes == 0) {
871 mark.style = ArdourCanvas::Ruler::Mark::Major;
872 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
874 mark.style = ArdourCanvas::Ruler::Mark::Minor;
875 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
878 snprintf (buf, sizeof(buf)," ");
879 mark.style = ArdourCanvas::Ruler::Mark::Micro;
883 marks.push_back (mark);
884 // Increment subframes by one
885 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
889 case timecode_show_frames:
890 // Find timecode time of this sample (pos)
891 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
892 // Go to next whole frame down
893 Timecode::frames_floor( timecode );
894 for (n = 0; n < timecode_nmarks; n++) {
895 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
896 if ((timecode.frames % timecode_mark_modulo) == 0) {
897 if (timecode.frames == 0) {
898 mark.style = ArdourCanvas::Ruler::Mark::Major;
900 mark.style = ArdourCanvas::Ruler::Mark::Minor;
903 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
905 snprintf (buf, sizeof(buf)," ");
906 mark.style = ArdourCanvas::Ruler::Mark::Micro;
910 marks.push_back (mark);
911 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
915 case timecode_show_seconds:
916 // Find timecode time of this sample (pos)
917 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
918 // Go to next whole second down
919 Timecode::seconds_floor( timecode );
920 for (n = 0; n < timecode_nmarks; n++) {
921 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
922 if ((timecode.seconds % timecode_mark_modulo) == 0) {
923 if (timecode.seconds == 0) {
924 mark.style = ArdourCanvas::Ruler::Mark::Major;
927 mark.style = ArdourCanvas::Ruler::Mark::Minor;
930 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
932 snprintf (buf, sizeof(buf)," ");
933 mark.style = ArdourCanvas::Ruler::Mark::Micro;
937 marks.push_back (mark);
938 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
942 case timecode_show_minutes:
943 //Find timecode time of this sample (pos)
944 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
945 // Go to next whole minute down
946 Timecode::minutes_floor( timecode );
947 for (n = 0; n < timecode_nmarks; n++) {
948 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
949 if ((timecode.minutes % timecode_mark_modulo) == 0) {
950 if (timecode.minutes == 0) {
951 mark.style = ArdourCanvas::Ruler::Mark::Major;
953 mark.style = ArdourCanvas::Ruler::Mark::Minor;
955 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
957 snprintf (buf, sizeof(buf)," ");
958 mark.style = ArdourCanvas::Ruler::Mark::Micro;
962 marks.push_back (mark);
963 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
966 case timecode_show_hours:
967 // Find timecode time of this sample (pos)
968 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
969 // Go to next whole hour down
970 Timecode::hours_floor( timecode );
971 for (n = 0; n < timecode_nmarks; n++) {
972 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
973 if ((timecode.hours % timecode_mark_modulo) == 0) {
974 mark.style = ArdourCanvas::Ruler::Mark::Major;
975 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
977 snprintf (buf, sizeof(buf)," ");
978 mark.style = ArdourCanvas::Ruler::Mark::Micro;
982 marks.push_back (mark);
983 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
986 case timecode_show_many_hours:
987 // Find timecode time of this sample (pos)
988 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
989 // Go to next whole hour down
990 Timecode::hours_floor (timecode);
992 for (n = 0; n < timecode_nmarks; ) {
993 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
994 if ((timecode.hours % timecode_mark_modulo) == 0) {
995 mark.style = ArdourCanvas::Ruler::Mark::Major;
996 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
999 marks.push_back (mark);
1002 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
1003 and doing it 1 hour at a time is just stupid (and slow).
1005 timecode.hours += timecode_mark_modulo;
1012 Editor::compute_bbt_ruler_scale (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, framepos_t lower, framepos_t upper)
1014 if (_session == 0) {
1018 std::vector<TempoMap::BBTPoint>::const_iterator i;
1019 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1020 framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor(_session->tempo_map().beat_at_frame (lower)));
1021 framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (_session->tempo_map().beat_at_frame (upper)) + 1.0);
1023 _session->bbt_time (beat_before_lower_pos, lower_beat);
1024 _session->bbt_time (beat_after_upper_pos, upper_beat);
1027 bbt_accent_modulo = 1;
1028 bbt_bar_helper_on = false;
1032 bbt_ruler_scale = bbt_show_many;
1034 switch (_snap_type) {
1035 case SnapToBeatDiv2:
1036 bbt_beat_subdivision = 2;
1038 case SnapToBeatDiv3:
1039 bbt_beat_subdivision = 3;
1041 case SnapToBeatDiv4:
1042 bbt_beat_subdivision = 4;
1044 case SnapToBeatDiv5:
1045 bbt_beat_subdivision = 5;
1046 bbt_accent_modulo = 2; // XXX YIKES
1048 case SnapToBeatDiv6:
1049 bbt_beat_subdivision = 6;
1050 bbt_accent_modulo = 2; // XXX YIKES
1052 case SnapToBeatDiv7:
1053 bbt_beat_subdivision = 7;
1054 bbt_accent_modulo = 2; // XXX YIKES
1056 case SnapToBeatDiv8:
1057 bbt_beat_subdivision = 8;
1058 bbt_accent_modulo = 2;
1060 case SnapToBeatDiv10:
1061 bbt_beat_subdivision = 10;
1062 bbt_accent_modulo = 2; // XXX YIKES
1064 case SnapToBeatDiv12:
1065 bbt_beat_subdivision = 12;
1066 bbt_accent_modulo = 3;
1068 case SnapToBeatDiv14:
1069 bbt_beat_subdivision = 14;
1070 bbt_accent_modulo = 3; // XXX YIKES!
1072 case SnapToBeatDiv16:
1073 bbt_beat_subdivision = 16;
1074 bbt_accent_modulo = 4;
1076 case SnapToBeatDiv20:
1077 bbt_beat_subdivision = 20;
1078 bbt_accent_modulo = 5;
1080 case SnapToBeatDiv24:
1081 bbt_beat_subdivision = 24;
1082 bbt_accent_modulo = 6;
1084 case SnapToBeatDiv28:
1085 bbt_beat_subdivision = 28;
1086 bbt_accent_modulo = 7;
1088 case SnapToBeatDiv32:
1089 bbt_beat_subdivision = 32;
1090 bbt_accent_modulo = 8;
1092 case SnapToBeatDiv64:
1093 bbt_beat_subdivision = 64;
1094 bbt_accent_modulo = 8;
1096 case SnapToBeatDiv128:
1097 bbt_beat_subdivision = 128;
1098 bbt_accent_modulo = 8;
1101 bbt_beat_subdivision = 4;
1104 if (distance (grid.begin(), grid.end()) == 0) {
1112 if ((*i).beat >= (*grid.begin()).beat) {
1113 bbt_bars = (*i).bar - (*grid.begin()).bar;
1115 bbt_bars = (*i).bar - (*grid.begin()).bar;
1118 beats = distance (grid.begin(), grid.end()) - bbt_bars;
1119 double beat_density = ((distance (grid.begin(), grid.end()) + 1) * ((double) (upper - lower) / (double) (1 + grid.back().frame - grid.front().frame))) / 5.0;
1120 /* Only show the bar helper if there aren't many bars on the screen */
1121 if ((bbt_bars < 2) || (beats < 5)) {
1122 bbt_bar_helper_on = true;
1125 if (beat_density > 8192) {
1126 bbt_ruler_scale = bbt_show_many;
1127 } else if (beat_density > 1024) {
1128 bbt_ruler_scale = bbt_show_64;
1129 } else if (beat_density > 500) {
1130 bbt_ruler_scale = bbt_show_16;
1131 } else if (beat_density > 100) {
1132 bbt_ruler_scale = bbt_show_4;
1133 } else if (beat_density > 10) {
1134 bbt_ruler_scale = bbt_show_1;
1135 } else if (beat_density > 2) {
1136 bbt_ruler_scale = bbt_show_beats;
1137 } else if (beat_density > 0.5) {
1138 bbt_ruler_scale = bbt_show_ticks;
1140 bbt_ruler_scale = bbt_show_ticks_detail;
1143 if ((bbt_ruler_scale == bbt_show_ticks_detail) && beats < 3) {
1144 bbt_ruler_scale = bbt_show_ticks_super_detail;
1149 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1151 ArdourCanvas::Ruler::Mark copy = marks.back();
1152 copy.label = newlabel;
1154 marks.push_back (copy);
1158 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1160 if (_session == 0) {
1164 std::vector<TempoMap::BBTPoint>::const_iterator i;
1169 Timecode::BBT_Time next_beat;
1174 double bbt_position_of_helper;
1175 bool i_am_accented = false;
1176 bool helper_active = false;
1177 ArdourCanvas::Ruler::Mark mark;
1179 std::vector<TempoMap::BBTPoint> grid;
1181 compute_current_bbt_points (grid, lower, upper);
1183 if (distance (grid.begin(), grid.end()) == 0) {
1187 switch (bbt_ruler_scale) {
1189 case bbt_show_beats:
1191 beats = distance (grid.begin(), grid.end());
1192 bbt_nmarks = beats + 2;
1195 mark.position = lower;
1196 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1197 marks.push_back (mark);
1199 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1201 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1202 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1203 edit_last_mark_label (marks, buf);
1204 helper_active = true;
1207 if ((*i).is_bar()) {
1208 mark.style = ArdourCanvas::Ruler::Mark::Major;
1209 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1210 } else if (((*i).beat % 2 == 1)) {
1211 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1214 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1218 mark.position = (*i).frame;
1219 marks.push_back (mark);
1225 case bbt_show_ticks:
1227 beats = distance (grid.begin(), grid.end());
1228 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1230 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1232 // could do marks.assign() here to preallocate
1235 mark.position = lower;
1236 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1237 marks.push_back (mark);
1239 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1241 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1242 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1243 edit_last_mark_label (marks, buf);
1244 helper_active = true;
1247 if ((*i).is_bar()) {
1248 mark.style = ArdourCanvas::Ruler::Mark::Major;
1249 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1251 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1252 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1254 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1258 mark.position = (*i).frame;
1259 marks.push_back (mark);
1263 /* Add the tick marks */
1264 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1265 tick = skip; // the first non-beat tick
1267 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1269 next_beat.beats = (*i).beat;
1270 next_beat.bars = (*i).bar;
1271 next_beat.ticks = tick;
1272 pos = _session->tempo_map().frame_time (next_beat);
1274 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1275 i_am_accented = true;
1278 mark.position = pos;
1280 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1281 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1283 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1285 i_am_accented = false;
1286 marks.push_back (mark);
1296 case bbt_show_ticks_detail:
1298 beats = distance (grid.begin(), grid.end());
1299 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1301 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1304 mark.position = lower;
1305 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1306 marks.push_back (mark);
1308 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1310 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1311 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1312 edit_last_mark_label (marks, buf);
1313 helper_active = true;
1316 if ((*i).is_bar()) {
1317 mark.style = ArdourCanvas::Ruler::Mark::Major;
1318 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1320 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1321 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1323 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1327 mark.position = (*i).frame;
1328 marks.push_back (mark);
1332 /* Add the tick marks */
1333 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1334 tick = skip; // the first non-beat tick
1337 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1339 next_beat.beats = (*i).beat;
1340 next_beat.bars = (*i).bar;
1341 next_beat.ticks = tick;
1342 pos = _session->tempo_map().frame_time (next_beat);
1344 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1345 i_am_accented = true;
1347 if (i_am_accented && (pos > bbt_position_of_helper)){
1348 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1354 mark.position = pos;
1356 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1357 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1359 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1361 i_am_accented = false;
1362 marks.push_back (mark);
1372 case bbt_show_ticks_super_detail:
1374 beats = distance (grid.begin(), grid.end());
1375 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1377 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1380 mark.position = lower;
1381 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1382 marks.push_back (mark);
1384 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1386 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1387 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1388 edit_last_mark_label (marks, buf);
1389 helper_active = true;
1392 if ((*i).is_bar()) {
1393 mark.style = ArdourCanvas::Ruler::Mark::Major;
1394 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1396 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1397 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1399 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1403 mark.position = (*i).frame;
1404 marks.push_back (mark);
1408 /* Add the tick marks */
1409 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1411 next_beat.beats = (*i).beat;
1412 next_beat.bars = (*i).bar;
1413 tick = skip; // the first non-beat tick
1415 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1417 next_beat.ticks = tick;
1418 pos = _session->tempo_map().frame_time (next_beat);
1419 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1420 i_am_accented = true;
1423 if (pos > bbt_position_of_helper) {
1424 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1430 mark.position = pos;
1432 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1433 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1435 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1437 i_am_accented = false;
1438 marks.push_back (mark);
1450 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1451 mark.style = ArdourCanvas::Ruler::Mark::Major;
1453 mark.position = lower;
1454 marks.push_back (mark);
1458 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1459 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1460 if ((*i).is_bar()) {
1461 if ((*i).bar % 64 == 1) {
1462 if ((*i).bar % 256 == 1) {
1463 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1464 mark.style = ArdourCanvas::Ruler::Mark::Major;
1467 if ((*i).bar % 256 == 129) {
1468 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1470 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1474 mark.position = (*i).frame;
1475 marks.push_back (mark);
1483 bbt_nmarks = (bbt_bars / 16) + 1;
1484 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1485 if ((*i).is_bar()) {
1486 if ((*i).bar % 16 == 1) {
1487 if ((*i).bar % 64 == 1) {
1488 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1489 mark.style = ArdourCanvas::Ruler::Mark::Major;
1492 if ((*i).bar % 64 == 33) {
1493 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1495 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1499 mark.position = (*i).frame;
1500 marks.push_back (mark);
1508 bbt_nmarks = (bbt_bars / 4) + 1;
1509 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1510 if ((*i).is_bar()) {
1511 if ((*i).bar % 4 == 1) {
1512 if ((*i).bar % 16 == 1) {
1513 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1514 mark.style = ArdourCanvas::Ruler::Mark::Major;
1517 if ((*i).bar % 16 == 9) {
1518 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1520 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1524 mark.position = (*i).frame;
1525 marks.push_back (mark);
1534 bbt_nmarks = bbt_bars + 2;
1535 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1536 if ((*i).is_bar()) {
1537 if ((*i).bar % 4 == 1) {
1538 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1539 mark.style = ArdourCanvas::Ruler::Mark::Major;
1542 if ((*i).bar % 4 == 3) {
1543 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1545 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1549 mark.position = (*i).frame;
1550 marks.push_back (mark);
1560 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1562 _samples_ruler_interval = (upper - lower) / 5;
1566 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1569 framepos_t const ilower = (framepos_t) floor (lower);
1573 ArdourCanvas::Ruler::Mark mark;
1575 if (_session == 0) {
1580 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1581 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1583 mark.position = pos;
1584 mark.style = ArdourCanvas::Ruler::Mark::Major;
1585 marks.push_back (mark);
1590 sample_to_clock_parts ( framepos_t sample,
1591 framepos_t sample_rate,
1605 hrs = left / (sample_rate * 60 * 60 * 1000);
1606 left -= hrs * sample_rate * 60 * 60 * 1000;
1607 mins = left / (sample_rate * 60 * 1000);
1608 left -= mins * sample_rate * 60 * 1000;
1609 secs = left / (sample_rate * 1000);
1610 left -= secs * sample_rate * 1000;
1611 millisecs = left / sample_rate;
1613 *millisecs_p = millisecs;
1622 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1624 framepos_t fr = _session->frame_rate() * 1000;
1627 if (_session == 0) {
1632 /* to prevent 'flashing' */
1633 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1639 framecnt_t const range = (upper - lower) * 1000;
1641 if (range <= (fr / 10)) { /* 0-0.1 second */
1642 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1643 minsec_ruler_scale = minsec_show_msecs;
1644 minsec_mark_modulo = 10;
1645 minsec_nmarks = 2 + (range / minsec_mark_interval);
1646 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1647 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1648 minsec_ruler_scale = minsec_show_msecs;
1649 minsec_mark_modulo = 100;
1650 minsec_nmarks = 2 + (range / minsec_mark_interval);
1651 } else if (range <= fr) { /* 0-1 second */
1652 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1653 minsec_ruler_scale = minsec_show_msecs;
1654 minsec_mark_modulo = 200;
1655 minsec_nmarks = 2 + (range / minsec_mark_interval);
1656 } else if (range <= 2 * fr) { /* 1-2 seconds */
1657 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1658 minsec_ruler_scale = minsec_show_msecs;
1659 minsec_mark_modulo = 500;
1660 minsec_nmarks = 2 + (range / minsec_mark_interval);
1661 } else if (range <= 8 * fr) { /* 2-5 seconds */
1662 minsec_mark_interval = fr / 5; /* show 2 seconds */
1663 minsec_ruler_scale = minsec_show_msecs;
1664 minsec_mark_modulo = 1000;
1665 minsec_nmarks = 2 + (range / minsec_mark_interval);
1666 } else if (range <= 16 * fr) { /* 8-16 seconds */
1667 minsec_mark_interval = fr; /* show 1 seconds */
1668 minsec_ruler_scale = minsec_show_seconds;
1669 minsec_mark_modulo = 2;
1670 minsec_nmarks = 2 + (range / minsec_mark_interval);
1671 } else if (range <= 30 * fr) { /* 10-30 seconds */
1672 minsec_mark_interval = fr; /* show 1 seconds */
1673 minsec_ruler_scale = minsec_show_seconds;
1674 minsec_mark_modulo = 5;
1675 minsec_nmarks = 2 + (range / minsec_mark_interval);
1676 } else if (range <= 60 * fr) { /* 30-60 seconds */
1677 minsec_mark_interval = fr; /* show 1 seconds */
1678 minsec_ruler_scale = minsec_show_seconds;
1679 minsec_mark_modulo = 5;
1680 minsec_nmarks = 2 + (range / minsec_mark_interval);
1681 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1682 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1683 minsec_ruler_scale = minsec_show_seconds;
1684 minsec_mark_modulo = 3;
1685 minsec_nmarks = 2 + (range / minsec_mark_interval);
1686 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1687 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1688 minsec_ruler_scale = minsec_show_seconds;
1689 minsec_mark_modulo = 30;
1690 minsec_nmarks = 2 + (range / minsec_mark_interval);
1691 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1692 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1693 minsec_ruler_scale = minsec_show_seconds;
1694 minsec_mark_modulo = 120;
1695 minsec_nmarks = 2 + (range / minsec_mark_interval);
1696 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1697 minsec_mark_interval = 60 * fr; /* show 1 minute */
1698 minsec_ruler_scale = minsec_show_minutes;
1699 minsec_mark_modulo = 5;
1700 minsec_nmarks = 2 + (range / minsec_mark_interval);
1701 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1702 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1703 minsec_ruler_scale = minsec_show_minutes;
1704 minsec_mark_modulo = 10;
1705 minsec_nmarks = 2 + (range / minsec_mark_interval);
1706 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1707 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1708 minsec_ruler_scale = minsec_show_minutes;
1709 minsec_mark_modulo = 30;
1710 minsec_nmarks = 2 + (range / minsec_mark_interval);
1711 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1712 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1713 minsec_ruler_scale = minsec_show_minutes;
1714 minsec_mark_modulo = 60;
1715 minsec_nmarks = 2 + (range / minsec_mark_interval);
1716 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1717 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1718 minsec_ruler_scale = minsec_show_hours;
1719 minsec_mark_modulo = 2;
1720 minsec_nmarks = 2 + (range / minsec_mark_interval);
1723 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1724 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1726 /* Normally we do not need to know anything about the width of the canvas
1727 to set the ruler scale, because the caller has already determined
1728 the width and set lower + upper arguments to this function to match that.
1730 But in this case, where the range defined by lower and uppper can vary
1731 substantially (anything from 24hrs+ to several billion years)
1732 trying to decide which tick marks to show does require us to know
1733 about the available width.
1736 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1737 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1738 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1739 minsec_ruler_scale = minsec_show_many_hours;
1744 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1748 long hrs, mins, secs, millisecs;
1751 ArdourCanvas::Ruler::Mark mark;
1753 if (_session == 0) {
1757 /* to prevent 'flashing' */
1758 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1759 lower = lower - spacer;
1764 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1766 switch (minsec_ruler_scale) {
1768 case minsec_show_msecs:
1769 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1770 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1771 if (millisecs % minsec_mark_modulo == 0) {
1772 if (millisecs == 0) {
1773 mark.style = ArdourCanvas::Ruler::Mark::Major;
1775 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1777 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1780 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1783 mark.position = pos/1000.0;
1784 marks.push_back (mark);
1788 case minsec_show_seconds:
1789 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1790 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1791 if (secs % minsec_mark_modulo == 0) {
1793 mark.style = ArdourCanvas::Ruler::Mark::Major;
1795 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1797 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1800 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1803 mark.position = pos/1000.0;
1804 marks.push_back (mark);
1808 case minsec_show_minutes:
1809 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1810 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1811 if (mins % minsec_mark_modulo == 0) {
1813 mark.style = ArdourCanvas::Ruler::Mark::Major;
1815 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1817 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1820 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1823 mark.position = pos/1000.0;
1824 marks.push_back (mark);
1828 case minsec_show_hours:
1829 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1830 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1831 if (hrs % minsec_mark_modulo == 0) {
1832 mark.style = ArdourCanvas::Ruler::Mark::Major;
1833 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1836 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1839 mark.position = pos/1000.0;
1840 marks.push_back (mark);
1844 case minsec_show_many_hours:
1845 for (n = 0; n < minsec_nmarks; ) {
1846 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1847 if (hrs % minsec_mark_modulo == 0) {
1848 mark.style = ArdourCanvas::Ruler::Mark::Major;
1849 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1851 mark.position = pos/1000.0;
1852 marks.push_back (mark);
1855 pos += minsec_mark_interval;