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 (samplepos_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->set_property (X_("timecode"), ruler_timecode_action->get_active());
298 node->set_property (X_("bbt"), ruler_bbt_action->get_active());
299 node->set_property (X_("samples"), ruler_samples_action->get_active());
300 node->set_property (X_("minsec"), ruler_minsec_action->get_active());
301 node->set_property (X_("tempo"), ruler_tempo_action->get_active());
302 node->set_property (X_("meter"), ruler_meter_action->get_active());
303 node->set_property (X_("marker"), ruler_marker_action->get_active());
304 node->set_property (X_("rangemarker"), ruler_range_action->get_active());
305 node->set_property (X_("transportmarker"), ruler_loop_punch_action->get_active());
306 node->set_property (X_("cdmarker"), ruler_cd_marker_action->get_active());
307 node->set_property (X_("videotl"), ruler_video_action->get_active());
309 _session->add_extra_xml (*node);
313 Editor::restore_ruler_visibility ()
315 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
317 no_ruler_shown_update = true;
321 if (node->get_property ("timecode", yn)) {
322 ruler_timecode_action->set_active (yn);
324 if (node->get_property ("bbt", yn)) {
325 ruler_bbt_action->set_active (yn);
327 if (node->get_property ("samples", yn)) {
328 ruler_samples_action->set_active (yn);
330 if (node->get_property ("minsec", yn)) {
331 ruler_minsec_action->set_active (yn);
333 if (node->get_property ("tempo", yn)) {
334 ruler_tempo_action->set_active (yn);
336 if (node->get_property ("meter", yn)) {
337 ruler_meter_action->set_active (yn);
339 if (node->get_property ("marker", yn)) {
340 ruler_marker_action->set_active (yn);
342 if (node->get_property ("rangemarker", yn)) {
343 ruler_range_action->set_active (yn);
345 if (node->get_property ("transportmarker", yn)) {
346 ruler_loop_punch_action->set_active (yn);
349 if (node->get_property ("cdmarker", yn)) {
350 ruler_cd_marker_action->set_active (yn);
352 // this _session doesn't yet know about the cdmarker ruler
353 // as a benefit to the user who doesn't know the feature exists, show the ruler if
354 // any cd marks exist
355 ruler_cd_marker_action->set_active (false);
356 const Locations::LocationList & locs = _session->locations()->list();
357 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
358 if ((*i)->is_cd_marker()) {
359 ruler_cd_marker_action->set_active (true);
365 if (node->get_property ("videotl", yn)) {
366 ruler_video_action->set_active (yn);
371 no_ruler_shown_update = false;
372 update_ruler_visibility ();
376 Editor::update_ruler_visibility ()
378 int visible_timebars = 0;
380 if (no_ruler_shown_update) {
384 /* the order of the timebars is fixed, so we have to go through each one
385 * and adjust its position depending on what is shown.
387 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
388 * loop/punch, cd markers, location markers
396 /* gtk update probs require this (damn) */
399 range_mark_label.hide();
400 transport_mark_label.hide();
401 cd_mark_label.hide();
403 videotl_label.hide();
406 if (ruler_minsec_action->get_active()) {
407 old_unit_pos = minsec_ruler->position().y;
408 if (tbpos != old_unit_pos) {
409 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
411 minsec_ruler->show();
413 tbpos += timebar_height;
414 tbgpos += timebar_height;
417 minsec_ruler->hide();
421 if (ruler_timecode_action->get_active()) {
422 old_unit_pos = timecode_ruler->position().y;
423 if (tbpos != old_unit_pos) {
424 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
426 timecode_ruler->show();
427 timecode_label.show();
428 tbpos += timebar_height;
429 tbgpos += timebar_height;
432 timecode_ruler->hide();
433 timecode_label.hide();
436 if (ruler_samples_action->get_active()) {
437 old_unit_pos = samples_ruler->position().y;
438 if (tbpos != old_unit_pos) {
439 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
441 samples_ruler->show();
442 samples_label.show();
443 tbpos += timebar_height;
444 tbgpos += timebar_height;
447 samples_ruler->hide();
448 samples_label.hide();
451 if (ruler_bbt_action->get_active()) {
452 old_unit_pos = bbt_ruler->position().y;
453 if (tbpos != old_unit_pos) {
454 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
458 tbpos += timebar_height;
459 tbgpos += timebar_height;
466 if (ruler_meter_action->get_active()) {
467 old_unit_pos = meter_group->position().y;
468 if (tbpos != old_unit_pos) {
469 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
473 tbpos += timebar_height;
474 tbgpos += timebar_height;
481 if (ruler_tempo_action->get_active()) {
482 old_unit_pos = tempo_group->position().y;
483 if (tbpos != old_unit_pos) {
484 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
488 tbpos += timebar_height;
489 tbgpos += timebar_height;
496 if (ruler_range_action->get_active()) {
497 old_unit_pos = range_marker_group->position().y;
498 if (tbpos != old_unit_pos) {
499 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
501 range_marker_group->show();
502 range_mark_label.show();
504 tbpos += timebar_height;
505 tbgpos += timebar_height;
508 range_marker_group->hide();
509 range_mark_label.hide();
512 if (ruler_loop_punch_action->get_active()) {
513 old_unit_pos = transport_marker_group->position().y;
514 if (tbpos != old_unit_pos) {
515 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
517 transport_marker_group->show();
518 transport_mark_label.show();
519 tbpos += timebar_height;
520 tbgpos += timebar_height;
523 transport_marker_group->hide();
524 transport_mark_label.hide();
527 if (ruler_cd_marker_action->get_active()) {
528 old_unit_pos = cd_marker_group->position().y;
529 if (tbpos != old_unit_pos) {
530 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
532 cd_marker_group->show();
533 cd_mark_label.show();
534 tbpos += timebar_height;
535 tbgpos += timebar_height;
537 // make sure all cd markers show up in their respective places
538 update_cd_marker_display();
540 cd_marker_group->hide();
541 cd_mark_label.hide();
542 // make sure all cd markers show up in their respective places
543 update_cd_marker_display();
546 if (ruler_marker_action->get_active()) {
547 old_unit_pos = marker_group->position().y;
548 if (tbpos != old_unit_pos) {
549 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
551 marker_group->show();
553 tbpos += timebar_height;
554 tbgpos += timebar_height;
557 marker_group->hide();
561 if (ruler_video_action->get_active()) {
562 old_unit_pos = videotl_group->position().y;
563 if (tbpos != old_unit_pos) {
564 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
566 videotl_group->show();
567 videotl_label.show();
568 tbpos += timebar_height * videotl_bar_height;
569 tbgpos += timebar_height * videotl_bar_height;
570 visible_timebars+=videotl_bar_height;
571 queue_visual_videotimeline_update();
573 videotl_group->hide();
574 videotl_label.hide();
575 update_video_timeline(true);
578 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
580 /* move hv_scroll_group (trackviews) to the end of the timebars
583 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
585 compute_fixed_ruler_scale ();
586 update_fixed_rulers();
587 redisplay_grid (false);
589 /* Changing ruler visibility means that any lines on markers might need updating */
590 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
591 i->second->setup_lines ();
596 Editor::update_just_timecode ()
598 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
604 samplepos_t rightmost_sample = _leftmost_sample + current_page_samples();
606 if (ruler_timecode_action->get_active()) {
607 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
612 Editor::compute_fixed_ruler_scale ()
618 if (ruler_timecode_action->get_active()) {
619 set_timecode_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
622 if (ruler_minsec_action->get_active()) {
623 set_minsec_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
626 if (ruler_samples_action->get_active()) {
627 set_samples_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
632 Editor::update_fixed_rulers ()
634 samplepos_t rightmost_sample;
640 compute_fixed_ruler_scale ();
642 _timecode_metric->units_per_pixel = samples_per_pixel;
643 _samples_metric->units_per_pixel = samples_per_pixel;
644 _minsec_metric->units_per_pixel = samples_per_pixel;
646 rightmost_sample = _leftmost_sample + current_page_samples();
648 /* these force a redraw, which in turn will force execution of the metric callbacks
649 to compute the relevant ticks to display.
652 if (ruler_timecode_action->get_active()) {
653 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
656 if (ruler_samples_action->get_active()) {
657 samples_ruler->set_range (_leftmost_sample, rightmost_sample);
660 if (ruler_minsec_action->get_active()) {
661 minsec_ruler->set_range (_leftmost_sample, rightmost_sample);
666 Editor::update_tempo_based_rulers ()
672 _bbt_metric->units_per_pixel = samples_per_pixel;
674 if (ruler_bbt_action->get_active()) {
675 bbt_ruler->set_range (_leftmost_sample, _leftmost_sample+current_page_samples());
681 Editor::set_timecode_ruler_scale (samplepos_t lower, samplepos_t upper)
692 fr = _session->sample_rate();
694 if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
695 lower = lower - spacer;
700 upper = upper + spacer;
701 samplecnt_t const range = upper - lower;
703 if (range < (2 * _session->samples_per_timecode_frame())) { /* 0 - 2 samples */
704 timecode_ruler_scale = timecode_show_bits;
705 timecode_mark_modulo = 20;
706 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
707 } else if (range <= (fr / 4)) { /* 2 samples - 0.250 second */
708 timecode_ruler_scale = timecode_show_samples;
709 timecode_mark_modulo = 1;
710 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
711 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
712 timecode_ruler_scale = timecode_show_samples;
713 timecode_mark_modulo = 2;
714 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
715 } else if (range <= fr) { /* 0.5-1 second */
716 timecode_ruler_scale = timecode_show_samples;
717 timecode_mark_modulo = 5;
718 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
719 } else if (range <= 2 * fr) { /* 1-2 seconds */
720 timecode_ruler_scale = timecode_show_samples;
721 timecode_mark_modulo = 10;
722 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
723 } else if (range <= 8 * fr) { /* 2-8 seconds */
724 timecode_ruler_scale = timecode_show_seconds;
725 timecode_mark_modulo = 1;
726 timecode_nmarks = 2 + (range / fr);
727 } else if (range <= 16 * fr) { /* 8-16 seconds */
728 timecode_ruler_scale = timecode_show_seconds;
729 timecode_mark_modulo = 2;
730 timecode_nmarks = 2 + (range / fr);
731 } else if (range <= 30 * fr) { /* 16-30 seconds */
732 timecode_ruler_scale = timecode_show_seconds;
733 timecode_mark_modulo = 5;
734 timecode_nmarks = 2 + (range / fr);
735 } else if (range <= 60 * fr) { /* 30-60 seconds */
736 timecode_ruler_scale = timecode_show_seconds;
737 timecode_mark_modulo = 5;
738 timecode_nmarks = 2 + (range / fr);
739 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
740 timecode_ruler_scale = timecode_show_seconds;
741 timecode_mark_modulo = 15;
742 timecode_nmarks = 2 + (range / fr);
743 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
744 timecode_ruler_scale = timecode_show_seconds;
745 timecode_mark_modulo = 30;
746 timecode_nmarks = 2 + (range / fr);
747 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
748 timecode_ruler_scale = timecode_show_minutes;
749 timecode_mark_modulo = 2;
750 timecode_nmarks = 2 + 10;
751 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
752 timecode_ruler_scale = timecode_show_minutes;
753 timecode_mark_modulo = 5;
754 timecode_nmarks = 2 + 30;
755 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
756 timecode_ruler_scale = timecode_show_minutes;
757 timecode_mark_modulo = 10;
758 timecode_nmarks = 2 + 60;
759 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
760 timecode_ruler_scale = timecode_show_minutes;
761 timecode_mark_modulo = 30;
762 timecode_nmarks = 2 + (60 * 4);
763 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
764 timecode_ruler_scale = timecode_show_hours;
765 timecode_mark_modulo = 1;
766 timecode_nmarks = 2 + 8;
767 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
768 timecode_ruler_scale = timecode_show_hours;
769 timecode_mark_modulo = 1;
770 timecode_nmarks = 2 + 24;
773 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
774 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
776 /* Normally we do not need to know anything about the width of the canvas
777 to set the ruler scale, because the caller has already determined
778 the width and set lower + upper arguments to this function to match that.
780 But in this case, where the range defined by lower and uppper can vary
781 substantially (basically anything from 24hrs+ to several billion years)
782 trying to decide which tick marks to show does require us to know
783 about the available width.
786 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
787 timecode_ruler_scale = timecode_show_many_hours;
788 timecode_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
793 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
797 Timecode::Time timecode;
800 ArdourCanvas::Ruler::Mark mark;
806 if (lower > (spacer = (samplecnt_t)(128 * Editor::get_current_zoom ()))) {
807 lower = lower - spacer;
812 pos = (samplecnt_t) floor (lower);
814 switch (timecode_ruler_scale) {
815 case timecode_show_bits:
816 // Find timecode time of this sample (pos) with subframe accuracy
817 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */);
818 for (n = 0; n < timecode_nmarks; n++) {
819 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */);
820 if ((timecode.subframes % timecode_mark_modulo) == 0) {
821 if (timecode.subframes == 0) {
822 mark.style = ArdourCanvas::Ruler::Mark::Major;
823 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
825 mark.style = ArdourCanvas::Ruler::Mark::Minor;
826 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
829 snprintf (buf, sizeof(buf)," ");
830 mark.style = ArdourCanvas::Ruler::Mark::Micro;
834 marks.push_back (mark);
835 // Increment subframes by one
836 Timecode::increment_subframes (timecode, _session->config.get_subframes_per_frame());
840 case timecode_show_samples:
841 // Find timecode time of this sample (pos)
842 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
843 // Go to next whole sample down
844 Timecode::frames_floot (timecode);
845 for (n = 0; n < timecode_nmarks; n++) {
846 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
847 if ((timecode.frames % timecode_mark_modulo) == 0) {
848 if (timecode.frames == 0) {
849 mark.style = ArdourCanvas::Ruler::Mark::Major;
851 mark.style = ArdourCanvas::Ruler::Mark::Minor;
854 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
856 snprintf (buf, sizeof(buf)," ");
857 mark.style = ArdourCanvas::Ruler::Mark::Micro;
861 marks.push_back (mark);
862 Timecode::increment (timecode, _session->config.get_subframes_per_frame());
866 case timecode_show_seconds:
867 // Find timecode time of this sample (pos)
868 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
869 // Go to next whole second down
870 Timecode::seconds_floor (timecode);
871 for (n = 0; n < timecode_nmarks; n++) {
872 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
873 if ((timecode.seconds % timecode_mark_modulo) == 0) {
874 if (timecode.seconds == 0) {
875 mark.style = ArdourCanvas::Ruler::Mark::Major;
878 mark.style = ArdourCanvas::Ruler::Mark::Minor;
881 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
883 snprintf (buf, sizeof(buf)," ");
884 mark.style = ArdourCanvas::Ruler::Mark::Micro;
888 marks.push_back (mark);
889 Timecode::increment_seconds (timecode, _session->config.get_subframes_per_frame());
893 case timecode_show_minutes:
894 //Find timecode time of this sample (pos)
895 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
896 // Go to next whole minute down
897 Timecode::minutes_floor (timecode);
898 for (n = 0; n < timecode_nmarks; n++) {
899 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
900 if ((timecode.minutes % timecode_mark_modulo) == 0) {
901 if (timecode.minutes == 0) {
902 mark.style = ArdourCanvas::Ruler::Mark::Major;
904 mark.style = ArdourCanvas::Ruler::Mark::Minor;
906 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
908 snprintf (buf, sizeof(buf)," ");
909 mark.style = ArdourCanvas::Ruler::Mark::Micro;
913 marks.push_back (mark);
914 Timecode::increment_minutes (timecode, _session->config.get_subframes_per_frame());
917 case timecode_show_hours:
918 // Find timecode time of this sample (pos)
919 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
920 // Go to next whole hour down
921 Timecode::hours_floor (timecode);
922 for (n = 0; n < timecode_nmarks; n++) {
923 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
924 if ((timecode.hours % timecode_mark_modulo) == 0) {
925 mark.style = ArdourCanvas::Ruler::Mark::Major;
926 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
928 snprintf (buf, sizeof(buf)," ");
929 mark.style = ArdourCanvas::Ruler::Mark::Micro;
933 marks.push_back (mark);
934 Timecode::increment_hours (timecode, _session->config.get_subframes_per_frame());
937 case timecode_show_many_hours:
938 // Find timecode time of this sample (pos)
939 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
940 // Go to next whole hour down
941 Timecode::hours_floor (timecode);
943 for (n = 0; n < timecode_nmarks;) {
944 _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
945 if ((timecode.hours % timecode_mark_modulo) == 0) {
946 mark.style = ArdourCanvas::Ruler::Mark::Major;
947 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
950 marks.push_back (mark);
953 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
954 * and doing it 1 hour at a time is just stupid (and slow).
956 timecode.hours += timecode_mark_modulo - (timecode.hours % timecode_mark_modulo);
963 Editor::compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper)
969 std::vector<TempoMap::BBTPoint>::const_iterator i;
970 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
971 double floor_lower_beat = floor(max (0.0, _session->tempo_map().beat_at_sample (lower)));
973 if (floor_lower_beat < 0.0) {
974 floor_lower_beat = 0.0;
977 const samplecnt_t beat_before_lower_pos = _session->tempo_map().sample_at_beat (floor_lower_beat);
978 const samplecnt_t beat_after_upper_pos = _session->tempo_map().sample_at_beat (floor (max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0);
980 _session->bbt_time (beat_before_lower_pos, lower_beat);
981 _session->bbt_time (beat_after_upper_pos, upper_beat);
984 bbt_accent_modulo = 1;
985 bbt_bar_helper_on = false;
989 bbt_ruler_scale = bbt_show_many;
991 switch (_grid_type) {
992 case GridTypeBeatDiv2:
993 bbt_beat_subdivision = 2;
995 case GridTypeBeatDiv3:
996 bbt_beat_subdivision = 3;
998 case GridTypeBeatDiv4:
999 bbt_beat_subdivision = 4;
1001 case GridTypeBeatDiv5:
1002 bbt_beat_subdivision = 5;
1003 bbt_accent_modulo = 2; // XXX YIKES
1005 case GridTypeBeatDiv6:
1006 bbt_beat_subdivision = 3;
1007 bbt_accent_modulo = 2; // XXX YIKES
1009 case GridTypeBeatDiv7:
1010 bbt_beat_subdivision = 7;
1011 bbt_accent_modulo = 2; // XXX YIKES
1013 case GridTypeBeatDiv8:
1014 bbt_beat_subdivision = 4;
1015 bbt_accent_modulo = 2;
1017 case GridTypeBeatDiv10:
1018 bbt_beat_subdivision = 5;
1019 bbt_accent_modulo = 2; // XXX YIKES
1021 case GridTypeBeatDiv12:
1022 bbt_beat_subdivision = 3;
1023 bbt_accent_modulo = 3;
1025 case GridTypeBeatDiv14:
1026 bbt_beat_subdivision = 7;
1027 bbt_accent_modulo = 3; // XXX YIKES!
1029 case GridTypeBeatDiv16:
1030 bbt_beat_subdivision = 4;
1031 bbt_accent_modulo = 4;
1033 case GridTypeBeatDiv20:
1034 bbt_beat_subdivision = 5;
1035 bbt_accent_modulo = 5;
1037 case GridTypeBeatDiv24:
1038 bbt_beat_subdivision = 6;
1039 bbt_accent_modulo = 6;
1041 case GridTypeBeatDiv28:
1042 bbt_beat_subdivision = 7;
1043 bbt_accent_modulo = 7;
1045 case GridTypeBeatDiv32:
1046 bbt_beat_subdivision = 4;
1047 bbt_accent_modulo = 8;
1051 bbt_beat_subdivision = 4;
1054 case GridTypeTimecode:
1055 case GridTypeMinSec:
1056 case GridTypeCDFrame:
1057 bbt_beat_subdivision = 4;
1061 const double ceil_upper_beat = floor (max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0;
1062 if (ceil_upper_beat == floor_lower_beat) {
1066 bbt_bars = _session->tempo_map().bbt_at_beat (ceil_upper_beat).bars - _session->tempo_map().bbt_at_beat (floor_lower_beat).bars;
1068 beats = (ceil_upper_beat - floor_lower_beat);// - bbt_bars; possible thinko; this fixes the problem (for me) where measure lines alternately appear&disappear while playing at certain zoom scales
1069 double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / 5.0;
1071 /* Only show the bar helper if there aren't many bars on the screen */
1072 if ((bbt_bars < 2) || (beats < 5)) {
1073 bbt_bar_helper_on = true;
1076 //set upper limits on the beat_density based on the user's grid selection
1077 if (_grid_type == GridTypeBar) {
1078 beat_density = fmax (beat_density, 16.01);
1079 } else if (_grid_type == GridTypeBeat) {
1080 beat_density = fmax (beat_density, 4.01);
1081 } else if (_grid_type == GridTypeBeatDiv2) {
1082 beat_density = fmax (beat_density, 2.01);
1083 } else if (_grid_type == GridTypeBeatDiv4) {
1084 beat_density = fmax (beat_density, 1.001);
1085 } else if (_grid_type == GridTypeBeatDiv8) {
1086 beat_density = fmax (beat_density, 0.501);
1087 } else if (_grid_type == GridTypeBeatDiv16) {
1088 beat_density = fmax (beat_density, 0.2501);
1089 } else if (_grid_type == GridTypeBeatDiv32) {
1090 beat_density = fmax (beat_density, 0.12501);
1093 if (beat_density > 2048) {
1094 bbt_ruler_scale = bbt_show_many;
1095 } else if (beat_density > 512) {
1096 bbt_ruler_scale = bbt_show_64;
1097 } else if (beat_density > 256) {
1098 bbt_ruler_scale = bbt_show_16;
1099 } else if (beat_density > 64) {
1100 bbt_ruler_scale = bbt_show_4;
1101 } else if (beat_density > 16) {
1102 bbt_ruler_scale = bbt_show_1;
1103 } else if (beat_density > 4) {
1104 bbt_ruler_scale = bbt_show_quarters;
1105 } else if (beat_density > 2) {
1106 bbt_ruler_scale = bbt_show_eighths;
1107 } else if (beat_density > 1) {
1108 bbt_ruler_scale = bbt_show_sixteenths;
1110 bbt_ruler_scale = bbt_show_thirtyseconds;
1115 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1117 ArdourCanvas::Ruler::Mark copy = marks.back();
1118 copy.label = newlabel;
1120 marks.push_back (copy);
1124 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1126 if (_session == 0) {
1130 std::vector<TempoMap::BBTPoint>::const_iterator i;
1135 Timecode::BBT_Time next_beat;
1140 double bbt_position_of_helper;
1141 bool i_am_accented = false;
1142 bool helper_active = false;
1143 ArdourCanvas::Ruler::Mark mark;
1145 std::vector<TempoMap::BBTPoint> grid;
1147 compute_current_bbt_points (grid, lower, upper);
1149 if (distance (grid.begin(), grid.end()) == 0) {
1153 switch (bbt_ruler_scale) {
1155 case bbt_show_quarters:
1157 beats = distance (grid.begin(), grid.end());
1158 bbt_nmarks = beats + 2;
1161 mark.position = lower;
1162 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1163 marks.push_back (mark);
1165 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1167 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1168 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1169 edit_last_mark_label (marks, buf);
1172 if ((*i).is_bar()) {
1173 mark.style = ArdourCanvas::Ruler::Mark::Major;
1174 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1175 } else if (((*i).beat % 2 == 1)) {
1176 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1179 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1183 mark.position = (*i).sample;
1184 marks.push_back (mark);
1190 case bbt_show_eighths:
1192 beats = distance (grid.begin(), grid.end());
1193 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1195 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1197 // could do marks.assign() here to preallocate
1200 mark.position = lower;
1201 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1202 marks.push_back (mark);
1204 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1206 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1207 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1208 edit_last_mark_label (marks, buf);
1209 helper_active = true;
1212 if ((*i).is_bar()) {
1213 mark.style = ArdourCanvas::Ruler::Mark::Major;
1214 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1216 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1217 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1219 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1223 mark.position = (*i).sample;
1224 marks.push_back (mark);
1228 /* Add the tick marks */
1229 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1230 tick = skip; // the first non-beat tick
1232 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1234 next_beat.beats = (*i).beat;
1235 next_beat.bars = (*i).bar;
1236 next_beat.ticks = tick;
1237 pos = _session->tempo_map().sample_at_bbt (next_beat);
1239 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1240 i_am_accented = true;
1243 mark.position = pos;
1245 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1246 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1248 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1250 i_am_accented = false;
1251 marks.push_back (mark);
1261 case bbt_show_sixteenths:
1263 beats = distance (grid.begin(), grid.end());
1264 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1266 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1269 mark.position = lower;
1270 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1271 marks.push_back (mark);
1273 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1275 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1276 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1277 edit_last_mark_label (marks, buf);
1278 helper_active = true;
1281 if ((*i).is_bar()) {
1282 mark.style = ArdourCanvas::Ruler::Mark::Major;
1283 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1285 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1286 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1288 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1292 mark.position = (*i).sample;
1293 marks.push_back (mark);
1297 /* Add the tick marks */
1298 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1299 tick = skip; // the first non-beat tick
1302 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1304 next_beat.beats = (*i).beat;
1305 next_beat.bars = (*i).bar;
1306 next_beat.ticks = tick;
1307 pos = _session->tempo_map().sample_at_bbt (next_beat);
1309 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1310 i_am_accented = true;
1312 if (i_am_accented && (pos > bbt_position_of_helper)){
1313 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1319 mark.position = pos;
1321 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1322 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1324 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1326 i_am_accented = false;
1327 marks.push_back (mark);
1337 case bbt_show_thirtyseconds:
1339 beats = distance (grid.begin(), grid.end());
1340 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1342 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1345 mark.position = lower;
1346 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1347 marks.push_back (mark);
1349 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1351 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1352 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1353 edit_last_mark_label (marks, buf);
1354 helper_active = true;
1357 if ((*i).is_bar()) {
1358 mark.style = ArdourCanvas::Ruler::Mark::Major;
1359 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1361 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1362 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1364 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1368 mark.position = (*i).sample;
1369 marks.push_back (mark);
1373 /* Add the tick marks */
1374 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1376 next_beat.beats = (*i).beat;
1377 next_beat.bars = (*i).bar;
1378 tick = skip; // the first non-beat tick
1380 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1382 next_beat.ticks = tick;
1383 pos = _session->tempo_map().sample_at_bbt (next_beat);
1384 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1385 i_am_accented = true;
1388 if (pos > bbt_position_of_helper) {
1389 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1395 mark.position = pos;
1397 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1398 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1400 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1402 i_am_accented = false;
1403 marks.push_back (mark);
1415 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars);
1416 mark.style = ArdourCanvas::Ruler::Mark::Major;
1418 mark.position = lower;
1419 marks.push_back (mark);
1423 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1424 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1425 if ((*i).is_bar()) {
1426 if ((*i).bar % 64 == 1) {
1427 if ((*i).bar % 256 == 1) {
1428 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1429 mark.style = ArdourCanvas::Ruler::Mark::Major;
1432 if ((*i).bar % 256 == 129) {
1433 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1435 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1439 mark.position = (*i).sample;
1440 marks.push_back (mark);
1448 bbt_nmarks = (bbt_bars / 16) + 1;
1449 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1450 if ((*i).is_bar()) {
1451 if ((*i).bar % 16 == 1) {
1452 if ((*i).bar % 64 == 1) {
1453 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1454 mark.style = ArdourCanvas::Ruler::Mark::Major;
1457 if ((*i).bar % 64 == 33) {
1458 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1460 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1464 mark.position = (*i).sample;
1465 marks.push_back (mark);
1473 bbt_nmarks = (bbt_bars / 4) + 1;
1474 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1475 if ((*i).is_bar()) {
1476 if ((*i).bar % 4 == 1) {
1477 if ((*i).bar % 16 == 1) {
1478 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1479 mark.style = ArdourCanvas::Ruler::Mark::Major;
1482 if ((*i).bar % 16 == 9) {
1483 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1485 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1489 mark.position = (*i).sample;
1490 marks.push_back (mark);
1499 bbt_nmarks = bbt_bars + 2;
1500 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1501 if ((*i).is_bar()) {
1502 if ((*i).bar % 4 == 1) {
1503 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1504 mark.style = ArdourCanvas::Ruler::Mark::Major;
1507 if ((*i).bar % 4 == 3) {
1508 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1510 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1514 mark.position = (*i).sample;
1515 marks.push_back (mark);
1525 Editor::set_samples_ruler_scale (samplepos_t lower, samplepos_t upper)
1527 _samples_ruler_interval = (upper - lower) / 5;
1531 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1534 samplepos_t const ilower = (samplepos_t) floor (lower);
1538 ArdourCanvas::Ruler::Mark mark;
1540 if (_session == 0) {
1545 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1546 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1548 mark.position = pos;
1549 mark.style = ArdourCanvas::Ruler::Mark::Major;
1550 marks.push_back (mark);
1555 sample_to_clock_parts (samplepos_t sample,
1556 samplepos_t sample_rate,
1569 hrs = left / (sample_rate * 60 * 60 * 1000);
1570 left -= hrs * sample_rate * 60 * 60 * 1000;
1571 mins = left / (sample_rate * 60 * 1000);
1572 left -= mins * sample_rate * 60 * 1000;
1573 secs = left / (sample_rate * 1000);
1574 left -= secs * sample_rate * 1000;
1575 millisecs = left / sample_rate;
1577 *millisecs_p = millisecs;
1586 Editor::set_minsec_ruler_scale (samplepos_t lower, samplepos_t upper)
1588 samplepos_t fr = _session->sample_rate() * 1000;
1591 if (_session == 0) {
1596 /* to prevent 'flashing' */
1597 if (lower > (spacer = (samplepos_t)(128 * Editor::get_current_zoom ()))) {
1603 samplecnt_t const range = (upper - lower) * 1000;
1605 if (range <= (fr / 10)) { /* 0-0.1 second */
1606 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1607 minsec_ruler_scale = minsec_show_msecs;
1608 minsec_mark_modulo = 10;
1609 minsec_nmarks = 2 + (range / minsec_mark_interval);
1610 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1611 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1612 minsec_ruler_scale = minsec_show_msecs;
1613 minsec_mark_modulo = 100;
1614 minsec_nmarks = 2 + (range / minsec_mark_interval);
1615 } else if (range <= fr) { /* 0-1 second */
1616 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1617 minsec_ruler_scale = minsec_show_msecs;
1618 minsec_mark_modulo = 200;
1619 minsec_nmarks = 2 + (range / minsec_mark_interval);
1620 } else if (range <= 2 * fr) { /* 1-2 seconds */
1621 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1622 minsec_ruler_scale = minsec_show_msecs;
1623 minsec_mark_modulo = 500;
1624 minsec_nmarks = 2 + (range / minsec_mark_interval);
1625 } else if (range <= 8 * fr) { /* 2-5 seconds */
1626 minsec_mark_interval = fr / 5; /* show 2 seconds */
1627 minsec_ruler_scale = minsec_show_msecs;
1628 minsec_mark_modulo = 1000;
1629 minsec_nmarks = 2 + (range / minsec_mark_interval);
1630 } else if (range <= 16 * fr) { /* 8-16 seconds */
1631 minsec_mark_interval = fr; /* show 1 seconds */
1632 minsec_ruler_scale = minsec_show_seconds;
1633 minsec_mark_modulo = 2;
1634 minsec_nmarks = 2 + (range / minsec_mark_interval);
1635 } else if (range <= 30 * fr) { /* 10-30 seconds */
1636 minsec_mark_interval = fr; /* show 1 seconds */
1637 minsec_ruler_scale = minsec_show_seconds;
1638 minsec_mark_modulo = 5;
1639 minsec_nmarks = 2 + (range / minsec_mark_interval);
1640 } else if (range <= 60 * fr) { /* 30-60 seconds */
1641 minsec_mark_interval = fr; /* show 1 seconds */
1642 minsec_ruler_scale = minsec_show_seconds;
1643 minsec_mark_modulo = 5;
1644 minsec_nmarks = 2 + (range / minsec_mark_interval);
1645 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1646 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1647 minsec_ruler_scale = minsec_show_seconds;
1648 minsec_mark_modulo = 3;
1649 minsec_nmarks = 2 + (range / minsec_mark_interval);
1650 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1651 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1652 minsec_ruler_scale = minsec_show_seconds;
1653 minsec_mark_modulo = 30;
1654 minsec_nmarks = 2 + (range / minsec_mark_interval);
1655 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1656 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1657 minsec_ruler_scale = minsec_show_seconds;
1658 minsec_mark_modulo = 120;
1659 minsec_nmarks = 2 + (range / minsec_mark_interval);
1660 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1661 minsec_mark_interval = 60 * fr; /* show 1 minute */
1662 minsec_ruler_scale = minsec_show_minutes;
1663 minsec_mark_modulo = 5;
1664 minsec_nmarks = 2 + (range / minsec_mark_interval);
1665 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1666 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1667 minsec_ruler_scale = minsec_show_minutes;
1668 minsec_mark_modulo = 10;
1669 minsec_nmarks = 2 + (range / minsec_mark_interval);
1670 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1671 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1672 minsec_ruler_scale = minsec_show_minutes;
1673 minsec_mark_modulo = 30;
1674 minsec_nmarks = 2 + (range / minsec_mark_interval);
1675 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1676 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1677 minsec_ruler_scale = minsec_show_minutes;
1678 minsec_mark_modulo = 60;
1679 minsec_nmarks = 2 + (range / minsec_mark_interval);
1680 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1681 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1682 minsec_ruler_scale = minsec_show_hours;
1683 minsec_mark_modulo = 2;
1684 minsec_nmarks = 2 + (range / minsec_mark_interval);
1687 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
1688 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1690 /* Normally we do not need to know anything about the width of the canvas
1691 to set the ruler scale, because the caller has already determined
1692 the width and set lower + upper arguments to this function to match that.
1694 But in this case, where the range defined by lower and uppper can vary
1695 substantially (anything from 24hrs+ to several billion years)
1696 trying to decide which tick marks to show does require us to know
1697 about the available width.
1700 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1701 minsec_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1702 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1703 minsec_ruler_scale = minsec_show_many_hours;
1708 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1712 long hrs, mins, secs, millisecs;
1715 ArdourCanvas::Ruler::Mark mark;
1717 if (_session == 0) {
1721 /* to prevent 'flashing' */
1722 if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
1723 lower = lower - spacer;
1728 if (minsec_mark_interval == 0) { //we got here too early; divide-by-zero imminent
1732 pos = (((1000 * (samplepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1734 switch (minsec_ruler_scale) {
1736 case minsec_show_msecs:
1737 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1738 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1739 if (millisecs % minsec_mark_modulo == 0) {
1740 if (millisecs == 0) {
1741 mark.style = ArdourCanvas::Ruler::Mark::Major;
1743 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1745 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1748 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1751 mark.position = pos/1000.0;
1752 marks.push_back (mark);
1756 case minsec_show_seconds:
1757 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1758 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1759 if (secs % minsec_mark_modulo == 0) {
1761 mark.style = ArdourCanvas::Ruler::Mark::Major;
1763 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1765 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1768 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1771 mark.position = pos/1000.0;
1772 marks.push_back (mark);
1776 case minsec_show_minutes:
1777 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1778 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1779 if (mins % minsec_mark_modulo == 0) {
1781 mark.style = ArdourCanvas::Ruler::Mark::Major;
1783 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1785 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1788 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1791 mark.position = pos/1000.0;
1792 marks.push_back (mark);
1796 case minsec_show_hours:
1797 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1798 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1799 if (hrs % minsec_mark_modulo == 0) {
1800 mark.style = ArdourCanvas::Ruler::Mark::Major;
1801 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1804 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1807 mark.position = pos/1000.0;
1808 marks.push_back (mark);
1812 case minsec_show_many_hours:
1813 for (n = 0; n < minsec_nmarks;) {
1814 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1815 if (hrs % minsec_mark_modulo == 0) {
1816 mark.style = ArdourCanvas::Ruler::Mark::Major;
1817 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1819 mark.position = pos/1000.0;
1820 marks.push_back (mark);
1823 pos += minsec_mark_interval;