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 ()
719 _bbt_metric->units_per_pixel = samples_per_pixel;
721 if (ruler_bbt_action->get_active()) {
722 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
728 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
739 fr = _session->frame_rate();
741 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
742 lower = lower - spacer;
747 upper = upper + spacer;
748 framecnt_t const range = upper - lower;
750 if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
751 timecode_ruler_scale = timecode_show_bits;
752 timecode_mark_modulo = 20;
753 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
754 } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
755 timecode_ruler_scale = timecode_show_frames;
756 timecode_mark_modulo = 1;
757 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
758 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
759 timecode_ruler_scale = timecode_show_frames;
760 timecode_mark_modulo = 2;
761 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
762 } else if (range <= fr) { /* 0.5-1 second */
763 timecode_ruler_scale = timecode_show_frames;
764 timecode_mark_modulo = 5;
765 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
766 } else if (range <= 2 * fr) { /* 1-2 seconds */
767 timecode_ruler_scale = timecode_show_frames;
768 timecode_mark_modulo = 10;
769 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
770 } else if (range <= 8 * fr) { /* 2-8 seconds */
771 timecode_ruler_scale = timecode_show_seconds;
772 timecode_mark_modulo = 1;
773 timecode_nmarks = 2 + (range / fr);
774 } else if (range <= 16 * fr) { /* 8-16 seconds */
775 timecode_ruler_scale = timecode_show_seconds;
776 timecode_mark_modulo = 2;
777 timecode_nmarks = 2 + (range / fr);
778 } else if (range <= 30 * fr) { /* 16-30 seconds */
779 timecode_ruler_scale = timecode_show_seconds;
780 timecode_mark_modulo = 5;
781 timecode_nmarks = 2 + (range / fr);
782 } else if (range <= 60 * fr) { /* 30-60 seconds */
783 timecode_ruler_scale = timecode_show_seconds;
784 timecode_mark_modulo = 5;
785 timecode_nmarks = 2 + (range / fr);
786 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
787 timecode_ruler_scale = timecode_show_seconds;
788 timecode_mark_modulo = 15;
789 timecode_nmarks = 2 + (range / fr);
790 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
791 timecode_ruler_scale = timecode_show_seconds;
792 timecode_mark_modulo = 30;
793 timecode_nmarks = 2 + (range / fr);
794 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
795 timecode_ruler_scale = timecode_show_minutes;
796 timecode_mark_modulo = 2;
797 timecode_nmarks = 2 + 10;
798 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
799 timecode_ruler_scale = timecode_show_minutes;
800 timecode_mark_modulo = 5;
801 timecode_nmarks = 2 + 30;
802 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
803 timecode_ruler_scale = timecode_show_minutes;
804 timecode_mark_modulo = 10;
805 timecode_nmarks = 2 + 60;
806 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
807 timecode_ruler_scale = timecode_show_minutes;
808 timecode_mark_modulo = 30;
809 timecode_nmarks = 2 + (60 * 4);
810 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
811 timecode_ruler_scale = timecode_show_hours;
812 timecode_mark_modulo = 1;
813 timecode_nmarks = 2 + 8;
814 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
815 timecode_ruler_scale = timecode_show_hours;
816 timecode_mark_modulo = 1;
817 timecode_nmarks = 2 + 24;
820 const framecnt_t hours_in_range = range / (60 * 60 * fr);
821 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
823 /* Normally we do not need to know anything about the width of the canvas
824 to set the ruler scale, because the caller has already determined
825 the width and set lower + upper arguments to this function to match that.
827 But in this case, where the range defined by lower and uppper can vary
828 substantially (basically anything from 24hrs+ to several billion years)
829 trying to decide which tick marks to show does require us to know
830 about the available width.
833 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
834 timecode_ruler_scale = timecode_show_many_hours;
835 timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
840 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
844 Timecode::Time timecode;
847 ArdourCanvas::Ruler::Mark mark;
853 if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
854 lower = lower - spacer;
859 pos = (framecnt_t) floor (lower);
861 switch (timecode_ruler_scale) {
862 case timecode_show_bits:
863 // Find timecode time of this sample (pos) with subframe accuracy
864 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
865 for (n = 0; n < timecode_nmarks; n++) {
866 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
867 if ((timecode.subframes % timecode_mark_modulo) == 0) {
868 if (timecode.subframes == 0) {
869 mark.style = ArdourCanvas::Ruler::Mark::Major;
870 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
872 mark.style = ArdourCanvas::Ruler::Mark::Minor;
873 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
876 snprintf (buf, sizeof(buf)," ");
877 mark.style = ArdourCanvas::Ruler::Mark::Micro;
881 marks.push_back (mark);
882 // Increment subframes by one
883 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
887 case timecode_show_frames:
888 // Find timecode time of this sample (pos)
889 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
890 // Go to next whole frame down
891 Timecode::frames_floor( timecode );
892 for (n = 0; n < timecode_nmarks; n++) {
893 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
894 if ((timecode.frames % timecode_mark_modulo) == 0) {
895 if (timecode.frames == 0) {
896 mark.style = ArdourCanvas::Ruler::Mark::Major;
898 mark.style = ArdourCanvas::Ruler::Mark::Minor;
901 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
903 snprintf (buf, sizeof(buf)," ");
904 mark.style = ArdourCanvas::Ruler::Mark::Micro;
908 marks.push_back (mark);
909 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
913 case timecode_show_seconds:
914 // Find timecode time of this sample (pos)
915 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
916 // Go to next whole second down
917 Timecode::seconds_floor( timecode );
918 for (n = 0; n < timecode_nmarks; n++) {
919 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
920 if ((timecode.seconds % timecode_mark_modulo) == 0) {
921 if (timecode.seconds == 0) {
922 mark.style = ArdourCanvas::Ruler::Mark::Major;
925 mark.style = ArdourCanvas::Ruler::Mark::Minor;
928 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
930 snprintf (buf, sizeof(buf)," ");
931 mark.style = ArdourCanvas::Ruler::Mark::Micro;
935 marks.push_back (mark);
936 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
940 case timecode_show_minutes:
941 //Find timecode time of this sample (pos)
942 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
943 // Go to next whole minute down
944 Timecode::minutes_floor( timecode );
945 for (n = 0; n < timecode_nmarks; n++) {
946 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
947 if ((timecode.minutes % timecode_mark_modulo) == 0) {
948 if (timecode.minutes == 0) {
949 mark.style = ArdourCanvas::Ruler::Mark::Major;
951 mark.style = ArdourCanvas::Ruler::Mark::Minor;
953 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
955 snprintf (buf, sizeof(buf)," ");
956 mark.style = ArdourCanvas::Ruler::Mark::Micro;
960 marks.push_back (mark);
961 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
964 case timecode_show_hours:
965 // Find timecode time of this sample (pos)
966 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
967 // Go to next whole hour down
968 Timecode::hours_floor( timecode );
969 for (n = 0; n < timecode_nmarks; n++) {
970 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
971 if ((timecode.hours % timecode_mark_modulo) == 0) {
972 mark.style = ArdourCanvas::Ruler::Mark::Major;
973 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
975 snprintf (buf, sizeof(buf)," ");
976 mark.style = ArdourCanvas::Ruler::Mark::Micro;
980 marks.push_back (mark);
981 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
984 case timecode_show_many_hours:
985 // Find timecode time of this sample (pos)
986 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
987 // Go to next whole hour down
988 Timecode::hours_floor (timecode);
990 for (n = 0; n < timecode_nmarks; ) {
991 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
992 if ((timecode.hours % timecode_mark_modulo) == 0) {
993 mark.style = ArdourCanvas::Ruler::Mark::Major;
994 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
997 marks.push_back (mark);
1000 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
1001 * and doing it 1 hour at a time is just stupid (and slow).
1003 timecode.hours += timecode_mark_modulo - (timecode.hours % timecode_mark_modulo);
1010 Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper)
1012 if (_session == 0) {
1016 std::vector<TempoMap::BBTPoint>::const_iterator i;
1017 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1018 double floor_lower_beat = floor(max (0.0, _session->tempo_map().beat_at_frame (lower)));
1020 if (floor_lower_beat < 0.0) {
1021 floor_lower_beat = 0.0;
1024 const framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor_lower_beat);
1025 const framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (max (0.0, _session->tempo_map().beat_at_frame (upper))) + 1.0);
1027 _session->bbt_time (beat_before_lower_pos, lower_beat);
1028 _session->bbt_time (beat_after_upper_pos, upper_beat);
1031 bbt_accent_modulo = 1;
1032 bbt_bar_helper_on = false;
1036 bbt_ruler_scale = bbt_show_many;
1038 switch (_snap_type) {
1039 case SnapToBeatDiv2:
1040 bbt_beat_subdivision = 2;
1042 case SnapToBeatDiv3:
1043 bbt_beat_subdivision = 3;
1045 case SnapToBeatDiv4:
1046 bbt_beat_subdivision = 4;
1048 case SnapToBeatDiv5:
1049 bbt_beat_subdivision = 5;
1050 bbt_accent_modulo = 2; // XXX YIKES
1052 case SnapToBeatDiv6:
1053 bbt_beat_subdivision = 6;
1054 bbt_accent_modulo = 2; // XXX YIKES
1056 case SnapToBeatDiv7:
1057 bbt_beat_subdivision = 7;
1058 bbt_accent_modulo = 2; // XXX YIKES
1060 case SnapToBeatDiv8:
1061 bbt_beat_subdivision = 8;
1062 bbt_accent_modulo = 2;
1064 case SnapToBeatDiv10:
1065 bbt_beat_subdivision = 10;
1066 bbt_accent_modulo = 2; // XXX YIKES
1068 case SnapToBeatDiv12:
1069 bbt_beat_subdivision = 12;
1070 bbt_accent_modulo = 3;
1072 case SnapToBeatDiv14:
1073 bbt_beat_subdivision = 14;
1074 bbt_accent_modulo = 3; // XXX YIKES!
1076 case SnapToBeatDiv16:
1077 bbt_beat_subdivision = 16;
1078 bbt_accent_modulo = 4;
1080 case SnapToBeatDiv20:
1081 bbt_beat_subdivision = 20;
1082 bbt_accent_modulo = 5;
1084 case SnapToBeatDiv24:
1085 bbt_beat_subdivision = 24;
1086 bbt_accent_modulo = 6;
1088 case SnapToBeatDiv28:
1089 bbt_beat_subdivision = 28;
1090 bbt_accent_modulo = 7;
1092 case SnapToBeatDiv32:
1093 bbt_beat_subdivision = 32;
1094 bbt_accent_modulo = 8;
1096 case SnapToBeatDiv64:
1097 bbt_beat_subdivision = 64;
1098 bbt_accent_modulo = 8;
1100 case SnapToBeatDiv128:
1101 bbt_beat_subdivision = 128;
1102 bbt_accent_modulo = 8;
1105 bbt_beat_subdivision = 4;
1109 const double ceil_upper_beat = floor (max (0.0, _session->tempo_map().beat_at_frame (upper))) + 1.0;
1110 if (ceil_upper_beat == floor_lower_beat) {
1114 bbt_bars = _session->tempo_map().bbt_at_beat (ceil_upper_beat).bars - _session->tempo_map().bbt_at_beat (floor_lower_beat).bars;
1116 beats = (ceil_upper_beat - floor_lower_beat) - bbt_bars;
1117 double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / 5.0;
1119 /* Only show the bar helper if there aren't many bars on the screen */
1120 if ((bbt_bars < 2) || (beats < 5)) {
1121 bbt_bar_helper_on = true;
1124 if (beat_density > 8192) {
1125 bbt_ruler_scale = bbt_show_many;
1126 } else if (beat_density > 1024) {
1127 bbt_ruler_scale = bbt_show_64;
1128 } else if (beat_density > 512) {
1129 bbt_ruler_scale = bbt_show_16;
1130 } else if (beat_density > 128) {
1131 bbt_ruler_scale = bbt_show_4;
1132 } else if (beat_density > 16) {
1133 bbt_ruler_scale = bbt_show_1;
1134 } else if (beat_density > 2) {
1135 bbt_ruler_scale = bbt_show_beats;
1136 } else if (beat_density > 0.5) {
1137 bbt_ruler_scale = bbt_show_ticks;
1139 bbt_ruler_scale = bbt_show_ticks_detail;
1142 if ((bbt_ruler_scale == bbt_show_ticks_detail) && beats < 3) {
1143 bbt_ruler_scale = bbt_show_ticks_super_detail;
1148 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1150 ArdourCanvas::Ruler::Mark copy = marks.back();
1151 copy.label = newlabel;
1153 marks.push_back (copy);
1157 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1159 if (_session == 0) {
1163 std::vector<TempoMap::BBTPoint>::const_iterator i;
1168 Timecode::BBT_Time next_beat;
1173 double bbt_position_of_helper;
1174 bool i_am_accented = false;
1175 bool helper_active = false;
1176 ArdourCanvas::Ruler::Mark mark;
1178 std::vector<TempoMap::BBTPoint> grid;
1180 compute_current_bbt_points (grid, lower, upper);
1182 if (distance (grid.begin(), grid.end()) == 0) {
1186 switch (bbt_ruler_scale) {
1188 case bbt_show_beats:
1190 beats = distance (grid.begin(), grid.end());
1191 bbt_nmarks = beats + 2;
1194 mark.position = lower;
1195 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1196 marks.push_back (mark);
1198 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1200 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1201 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1202 edit_last_mark_label (marks, buf);
1203 helper_active = true;
1206 if ((*i).is_bar()) {
1207 mark.style = ArdourCanvas::Ruler::Mark::Major;
1208 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1209 } else if (((*i).beat % 2 == 1)) {
1210 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1213 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1217 mark.position = (*i).frame;
1218 marks.push_back (mark);
1224 case bbt_show_ticks:
1226 beats = distance (grid.begin(), grid.end());
1227 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1229 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1231 // could do marks.assign() here to preallocate
1234 mark.position = lower;
1235 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1236 marks.push_back (mark);
1238 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1240 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1241 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1242 edit_last_mark_label (marks, buf);
1243 helper_active = true;
1246 if ((*i).is_bar()) {
1247 mark.style = ArdourCanvas::Ruler::Mark::Major;
1248 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1250 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1251 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1253 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1257 mark.position = (*i).frame;
1258 marks.push_back (mark);
1262 /* Add the tick marks */
1263 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1264 tick = skip; // the first non-beat tick
1266 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1268 next_beat.beats = (*i).beat;
1269 next_beat.bars = (*i).bar;
1270 next_beat.ticks = tick;
1271 pos = _session->tempo_map().frame_at_bbt (next_beat);
1273 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1274 i_am_accented = true;
1277 mark.position = pos;
1279 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1280 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1282 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1284 i_am_accented = false;
1285 marks.push_back (mark);
1295 case bbt_show_ticks_detail:
1297 beats = distance (grid.begin(), grid.end());
1298 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1300 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1303 mark.position = lower;
1304 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1305 marks.push_back (mark);
1307 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1309 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1310 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1311 edit_last_mark_label (marks, buf);
1312 helper_active = true;
1315 if ((*i).is_bar()) {
1316 mark.style = ArdourCanvas::Ruler::Mark::Major;
1317 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1319 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1320 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1322 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1326 mark.position = (*i).frame;
1327 marks.push_back (mark);
1331 /* Add the tick marks */
1332 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1333 tick = skip; // the first non-beat tick
1336 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1338 next_beat.beats = (*i).beat;
1339 next_beat.bars = (*i).bar;
1340 next_beat.ticks = tick;
1341 pos = _session->tempo_map().frame_at_bbt (next_beat);
1343 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1344 i_am_accented = true;
1346 if (i_am_accented && (pos > bbt_position_of_helper)){
1347 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1353 mark.position = pos;
1355 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1356 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1358 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1360 i_am_accented = false;
1361 marks.push_back (mark);
1371 case bbt_show_ticks_super_detail:
1373 beats = distance (grid.begin(), grid.end());
1374 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1376 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1379 mark.position = lower;
1380 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1381 marks.push_back (mark);
1383 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1385 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1386 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1387 edit_last_mark_label (marks, buf);
1388 helper_active = true;
1391 if ((*i).is_bar()) {
1392 mark.style = ArdourCanvas::Ruler::Mark::Major;
1393 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1395 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1396 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1398 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1402 mark.position = (*i).frame;
1403 marks.push_back (mark);
1407 /* Add the tick marks */
1408 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1410 next_beat.beats = (*i).beat;
1411 next_beat.bars = (*i).bar;
1412 tick = skip; // the first non-beat tick
1414 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1416 next_beat.ticks = tick;
1417 pos = _session->tempo_map().frame_at_bbt (next_beat);
1418 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1419 i_am_accented = true;
1422 if (pos > bbt_position_of_helper) {
1423 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1429 mark.position = pos;
1431 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1432 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1434 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1436 i_am_accented = false;
1437 marks.push_back (mark);
1449 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1450 mark.style = ArdourCanvas::Ruler::Mark::Major;
1452 mark.position = lower;
1453 marks.push_back (mark);
1457 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1458 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1459 if ((*i).is_bar()) {
1460 if ((*i).bar % 64 == 1) {
1461 if ((*i).bar % 256 == 1) {
1462 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1463 mark.style = ArdourCanvas::Ruler::Mark::Major;
1466 if ((*i).bar % 256 == 129) {
1467 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1469 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1473 mark.position = (*i).frame;
1474 marks.push_back (mark);
1482 bbt_nmarks = (bbt_bars / 16) + 1;
1483 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1484 if ((*i).is_bar()) {
1485 if ((*i).bar % 16 == 1) {
1486 if ((*i).bar % 64 == 1) {
1487 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1488 mark.style = ArdourCanvas::Ruler::Mark::Major;
1491 if ((*i).bar % 64 == 33) {
1492 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1494 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1498 mark.position = (*i).frame;
1499 marks.push_back (mark);
1507 bbt_nmarks = (bbt_bars / 4) + 1;
1508 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1509 if ((*i).is_bar()) {
1510 if ((*i).bar % 4 == 1) {
1511 if ((*i).bar % 16 == 1) {
1512 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1513 mark.style = ArdourCanvas::Ruler::Mark::Major;
1516 if ((*i).bar % 16 == 9) {
1517 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1519 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1523 mark.position = (*i).frame;
1524 marks.push_back (mark);
1533 bbt_nmarks = bbt_bars + 2;
1534 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1535 if ((*i).is_bar()) {
1536 if ((*i).bar % 4 == 1) {
1537 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1538 mark.style = ArdourCanvas::Ruler::Mark::Major;
1541 if ((*i).bar % 4 == 3) {
1542 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1544 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1548 mark.position = (*i).frame;
1549 marks.push_back (mark);
1559 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1561 _samples_ruler_interval = (upper - lower) / 5;
1565 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1568 framepos_t const ilower = (framepos_t) floor (lower);
1572 ArdourCanvas::Ruler::Mark mark;
1574 if (_session == 0) {
1579 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1580 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1582 mark.position = pos;
1583 mark.style = ArdourCanvas::Ruler::Mark::Major;
1584 marks.push_back (mark);
1589 sample_to_clock_parts ( framepos_t sample,
1590 framepos_t sample_rate,
1604 hrs = left / (sample_rate * 60 * 60 * 1000);
1605 left -= hrs * sample_rate * 60 * 60 * 1000;
1606 mins = left / (sample_rate * 60 * 1000);
1607 left -= mins * sample_rate * 60 * 1000;
1608 secs = left / (sample_rate * 1000);
1609 left -= secs * sample_rate * 1000;
1610 millisecs = left / sample_rate;
1612 *millisecs_p = millisecs;
1621 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1623 framepos_t fr = _session->frame_rate() * 1000;
1626 if (_session == 0) {
1631 /* to prevent 'flashing' */
1632 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1638 framecnt_t const range = (upper - lower) * 1000;
1640 if (range <= (fr / 10)) { /* 0-0.1 second */
1641 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1642 minsec_ruler_scale = minsec_show_msecs;
1643 minsec_mark_modulo = 10;
1644 minsec_nmarks = 2 + (range / minsec_mark_interval);
1645 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1646 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1647 minsec_ruler_scale = minsec_show_msecs;
1648 minsec_mark_modulo = 100;
1649 minsec_nmarks = 2 + (range / minsec_mark_interval);
1650 } else if (range <= fr) { /* 0-1 second */
1651 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1652 minsec_ruler_scale = minsec_show_msecs;
1653 minsec_mark_modulo = 200;
1654 minsec_nmarks = 2 + (range / minsec_mark_interval);
1655 } else if (range <= 2 * fr) { /* 1-2 seconds */
1656 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1657 minsec_ruler_scale = minsec_show_msecs;
1658 minsec_mark_modulo = 500;
1659 minsec_nmarks = 2 + (range / minsec_mark_interval);
1660 } else if (range <= 8 * fr) { /* 2-5 seconds */
1661 minsec_mark_interval = fr / 5; /* show 2 seconds */
1662 minsec_ruler_scale = minsec_show_msecs;
1663 minsec_mark_modulo = 1000;
1664 minsec_nmarks = 2 + (range / minsec_mark_interval);
1665 } else if (range <= 16 * fr) { /* 8-16 seconds */
1666 minsec_mark_interval = fr; /* show 1 seconds */
1667 minsec_ruler_scale = minsec_show_seconds;
1668 minsec_mark_modulo = 2;
1669 minsec_nmarks = 2 + (range / minsec_mark_interval);
1670 } else if (range <= 30 * fr) { /* 10-30 seconds */
1671 minsec_mark_interval = fr; /* show 1 seconds */
1672 minsec_ruler_scale = minsec_show_seconds;
1673 minsec_mark_modulo = 5;
1674 minsec_nmarks = 2 + (range / minsec_mark_interval);
1675 } else if (range <= 60 * fr) { /* 30-60 seconds */
1676 minsec_mark_interval = fr; /* show 1 seconds */
1677 minsec_ruler_scale = minsec_show_seconds;
1678 minsec_mark_modulo = 5;
1679 minsec_nmarks = 2 + (range / minsec_mark_interval);
1680 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1681 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1682 minsec_ruler_scale = minsec_show_seconds;
1683 minsec_mark_modulo = 3;
1684 minsec_nmarks = 2 + (range / minsec_mark_interval);
1685 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1686 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1687 minsec_ruler_scale = minsec_show_seconds;
1688 minsec_mark_modulo = 30;
1689 minsec_nmarks = 2 + (range / minsec_mark_interval);
1690 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1691 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1692 minsec_ruler_scale = minsec_show_seconds;
1693 minsec_mark_modulo = 120;
1694 minsec_nmarks = 2 + (range / minsec_mark_interval);
1695 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1696 minsec_mark_interval = 60 * fr; /* show 1 minute */
1697 minsec_ruler_scale = minsec_show_minutes;
1698 minsec_mark_modulo = 5;
1699 minsec_nmarks = 2 + (range / minsec_mark_interval);
1700 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1701 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1702 minsec_ruler_scale = minsec_show_minutes;
1703 minsec_mark_modulo = 10;
1704 minsec_nmarks = 2 + (range / minsec_mark_interval);
1705 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1706 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1707 minsec_ruler_scale = minsec_show_minutes;
1708 minsec_mark_modulo = 30;
1709 minsec_nmarks = 2 + (range / minsec_mark_interval);
1710 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1711 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1712 minsec_ruler_scale = minsec_show_minutes;
1713 minsec_mark_modulo = 60;
1714 minsec_nmarks = 2 + (range / minsec_mark_interval);
1715 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1716 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1717 minsec_ruler_scale = minsec_show_hours;
1718 minsec_mark_modulo = 2;
1719 minsec_nmarks = 2 + (range / minsec_mark_interval);
1722 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1723 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1725 /* Normally we do not need to know anything about the width of the canvas
1726 to set the ruler scale, because the caller has already determined
1727 the width and set lower + upper arguments to this function to match that.
1729 But in this case, where the range defined by lower and uppper can vary
1730 substantially (anything from 24hrs+ to several billion years)
1731 trying to decide which tick marks to show does require us to know
1732 about the available width.
1735 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1736 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1737 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1738 minsec_ruler_scale = minsec_show_many_hours;
1743 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1747 long hrs, mins, secs, millisecs;
1750 ArdourCanvas::Ruler::Mark mark;
1752 if (_session == 0) {
1756 /* to prevent 'flashing' */
1757 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1758 lower = lower - spacer;
1763 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1765 switch (minsec_ruler_scale) {
1767 case minsec_show_msecs:
1768 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1769 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1770 if (millisecs % minsec_mark_modulo == 0) {
1771 if (millisecs == 0) {
1772 mark.style = ArdourCanvas::Ruler::Mark::Major;
1774 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1776 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1779 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1782 mark.position = pos/1000.0;
1783 marks.push_back (mark);
1787 case minsec_show_seconds:
1788 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1789 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1790 if (secs % minsec_mark_modulo == 0) {
1792 mark.style = ArdourCanvas::Ruler::Mark::Major;
1794 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1796 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1799 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1802 mark.position = pos/1000.0;
1803 marks.push_back (mark);
1807 case minsec_show_minutes:
1808 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1809 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1810 if (mins % minsec_mark_modulo == 0) {
1812 mark.style = ArdourCanvas::Ruler::Mark::Major;
1814 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1816 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1819 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1822 mark.position = pos/1000.0;
1823 marks.push_back (mark);
1827 case minsec_show_hours:
1828 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1829 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1830 if (hrs % minsec_mark_modulo == 0) {
1831 mark.style = ArdourCanvas::Ruler::Mark::Major;
1832 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1835 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1838 mark.position = pos/1000.0;
1839 marks.push_back (mark);
1843 case minsec_show_many_hours:
1844 for (n = 0; n < minsec_nmarks; ) {
1845 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1846 if (hrs % minsec_mark_modulo == 0) {
1847 mark.style = ArdourCanvas::Ruler::Mark::Major;
1848 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1850 mark.position = pos/1000.0;
1851 marks.push_back (mark);
1854 pos += minsec_mark_interval;