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);
310 _session->set_dirty ();
314 Editor::restore_ruler_visibility ()
316 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
318 no_ruler_shown_update = true;
322 if (node->get_property ("timecode", yn)) {
323 ruler_timecode_action->set_active (yn);
325 if (node->get_property ("bbt", yn)) {
326 ruler_bbt_action->set_active (yn);
328 if (node->get_property ("samples", yn)) {
329 ruler_samples_action->set_active (yn);
331 if (node->get_property ("minsec", yn)) {
332 ruler_minsec_action->set_active (yn);
334 if (node->get_property ("tempo", yn)) {
335 ruler_tempo_action->set_active (yn);
337 if (node->get_property ("meter", yn)) {
338 ruler_meter_action->set_active (yn);
340 if (node->get_property ("marker", yn)) {
341 ruler_marker_action->set_active (yn);
343 if (node->get_property ("rangemarker", yn)) {
344 ruler_range_action->set_active (yn);
346 if (node->get_property ("transportmarker", yn)) {
347 ruler_loop_punch_action->set_active (yn);
350 if (node->get_property ("cdmarker", yn)) {
351 ruler_cd_marker_action->set_active (yn);
353 // this _session doesn't yet know about the cdmarker ruler
354 // as a benefit to the user who doesn't know the feature exists, show the ruler if
355 // any cd marks exist
356 ruler_cd_marker_action->set_active (false);
357 const Locations::LocationList & locs = _session->locations()->list();
358 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
359 if ((*i)->is_cd_marker()) {
360 ruler_cd_marker_action->set_active (true);
366 if (node->get_property ("videotl", yn)) {
367 ruler_video_action->set_active (yn);
372 no_ruler_shown_update = false;
373 update_ruler_visibility ();
377 Editor::update_ruler_visibility ()
379 int visible_timebars = 0;
381 if (no_ruler_shown_update) {
385 /* the order of the timebars is fixed, so we have to go through each one
386 * and adjust its position depending on what is shown.
388 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
389 * loop/punch, cd markers, location markers
397 /* gtk update probs require this (damn) */
400 range_mark_label.hide();
401 transport_mark_label.hide();
402 cd_mark_label.hide();
404 videotl_label.hide();
407 if (ruler_minsec_action->get_active()) {
408 old_unit_pos = minsec_ruler->position().y;
409 if (tbpos != old_unit_pos) {
410 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
412 minsec_ruler->show();
414 tbpos += timebar_height;
415 tbgpos += timebar_height;
418 minsec_ruler->hide();
422 if (ruler_timecode_action->get_active()) {
423 old_unit_pos = timecode_ruler->position().y;
424 if (tbpos != old_unit_pos) {
425 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
427 timecode_ruler->show();
428 timecode_label.show();
429 tbpos += timebar_height;
430 tbgpos += timebar_height;
433 timecode_ruler->hide();
434 timecode_label.hide();
437 if (ruler_samples_action->get_active()) {
438 old_unit_pos = samples_ruler->position().y;
439 if (tbpos != old_unit_pos) {
440 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
442 samples_ruler->show();
443 samples_label.show();
444 tbpos += timebar_height;
445 tbgpos += timebar_height;
448 samples_ruler->hide();
449 samples_label.hide();
452 if (ruler_bbt_action->get_active()) {
453 old_unit_pos = bbt_ruler->position().y;
454 if (tbpos != old_unit_pos) {
455 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
459 tbpos += timebar_height;
460 tbgpos += timebar_height;
467 if (ruler_meter_action->get_active()) {
468 old_unit_pos = meter_group->position().y;
469 if (tbpos != old_unit_pos) {
470 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
474 tbpos += timebar_height;
475 tbgpos += timebar_height;
482 if (ruler_tempo_action->get_active()) {
483 old_unit_pos = tempo_group->position().y;
484 if (tbpos != old_unit_pos) {
485 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
489 tbpos += timebar_height;
490 tbgpos += timebar_height;
497 if (ruler_range_action->get_active()) {
498 old_unit_pos = range_marker_group->position().y;
499 if (tbpos != old_unit_pos) {
500 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
502 range_marker_group->show();
503 range_mark_label.show();
505 tbpos += timebar_height;
506 tbgpos += timebar_height;
509 range_marker_group->hide();
510 range_mark_label.hide();
513 if (ruler_loop_punch_action->get_active()) {
514 old_unit_pos = transport_marker_group->position().y;
515 if (tbpos != old_unit_pos) {
516 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
518 transport_marker_group->show();
519 transport_mark_label.show();
520 tbpos += timebar_height;
521 tbgpos += timebar_height;
524 transport_marker_group->hide();
525 transport_mark_label.hide();
528 if (ruler_cd_marker_action->get_active()) {
529 old_unit_pos = cd_marker_group->position().y;
530 if (tbpos != old_unit_pos) {
531 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
533 cd_marker_group->show();
534 cd_mark_label.show();
535 tbpos += timebar_height;
536 tbgpos += timebar_height;
538 // make sure all cd markers show up in their respective places
539 update_cd_marker_display();
541 cd_marker_group->hide();
542 cd_mark_label.hide();
543 // make sure all cd markers show up in their respective places
544 update_cd_marker_display();
547 if (ruler_marker_action->get_active()) {
548 old_unit_pos = marker_group->position().y;
549 if (tbpos != old_unit_pos) {
550 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
552 marker_group->show();
554 tbpos += timebar_height;
555 tbgpos += timebar_height;
558 marker_group->hide();
562 if (ruler_video_action->get_active()) {
563 old_unit_pos = videotl_group->position().y;
564 if (tbpos != old_unit_pos) {
565 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
567 videotl_group->show();
568 videotl_label.show();
569 tbpos += timebar_height * videotl_bar_height;
570 tbgpos += timebar_height * videotl_bar_height;
571 visible_timebars+=videotl_bar_height;
572 queue_visual_videotimeline_update();
574 videotl_group->hide();
575 videotl_label.hide();
576 update_video_timeline(true);
579 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
581 /* move hv_scroll_group (trackviews) to the end of the timebars
584 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
586 compute_fixed_ruler_scale ();
587 update_fixed_rulers();
588 redisplay_grid (false);
590 /* Changing ruler visibility means that any lines on markers might need updating */
591 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
592 i->second->setup_lines ();
597 Editor::update_just_timecode ()
599 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
605 samplepos_t rightmost_sample = _leftmost_sample + current_page_samples();
607 if (ruler_timecode_action->get_active()) {
608 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
613 Editor::compute_fixed_ruler_scale ()
619 if (ruler_timecode_action->get_active()) {
620 set_timecode_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
623 if (ruler_minsec_action->get_active()) {
624 set_minsec_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
627 if (ruler_samples_action->get_active()) {
628 set_samples_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
633 Editor::update_fixed_rulers ()
635 samplepos_t rightmost_sample;
641 compute_fixed_ruler_scale ();
643 _timecode_metric->units_per_pixel = samples_per_pixel;
644 _samples_metric->units_per_pixel = samples_per_pixel;
645 _minsec_metric->units_per_pixel = samples_per_pixel;
647 rightmost_sample = _leftmost_sample + current_page_samples();
649 /* these force a redraw, which in turn will force execution of the metric callbacks
650 to compute the relevant ticks to display.
653 if (ruler_timecode_action->get_active()) {
654 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
657 if (ruler_samples_action->get_active()) {
658 samples_ruler->set_range (_leftmost_sample, rightmost_sample);
661 if (ruler_minsec_action->get_active()) {
662 minsec_ruler->set_range (_leftmost_sample, rightmost_sample);
667 Editor::update_tempo_based_rulers ()
673 _bbt_metric->units_per_pixel = samples_per_pixel;
675 if (ruler_bbt_action->get_active()) {
676 bbt_ruler->set_range (_leftmost_sample, _leftmost_sample+current_page_samples());
682 Editor::set_timecode_ruler_scale (samplepos_t lower, samplepos_t upper)
693 fr = _session->sample_rate();
695 if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
696 lower = lower - spacer;
701 upper = upper + spacer;
702 samplecnt_t const range = upper - lower;
704 if (range < (2 * _session->samples_per_timecode_frame())) { /* 0 - 2 samples */
705 timecode_ruler_scale = timecode_show_bits;
706 timecode_mark_modulo = 20;
707 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
708 } else if (range <= (fr / 4)) { /* 2 samples - 0.250 second */
709 timecode_ruler_scale = timecode_show_samples;
710 timecode_mark_modulo = 1;
711 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
712 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
713 timecode_ruler_scale = timecode_show_samples;
714 timecode_mark_modulo = 2;
715 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
716 } else if (range <= fr) { /* 0.5-1 second */
717 timecode_ruler_scale = timecode_show_samples;
718 timecode_mark_modulo = 5;
719 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
720 } else if (range <= 2 * fr) { /* 1-2 seconds */
721 timecode_ruler_scale = timecode_show_samples;
722 timecode_mark_modulo = 10;
723 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
724 } else if (range <= 8 * fr) { /* 2-8 seconds */
725 timecode_ruler_scale = timecode_show_seconds;
726 timecode_mark_modulo = 1;
727 timecode_nmarks = 2 + (range / fr);
728 } else if (range <= 16 * fr) { /* 8-16 seconds */
729 timecode_ruler_scale = timecode_show_seconds;
730 timecode_mark_modulo = 2;
731 timecode_nmarks = 2 + (range / fr);
732 } else if (range <= 30 * fr) { /* 16-30 seconds */
733 timecode_ruler_scale = timecode_show_seconds;
734 timecode_mark_modulo = 5;
735 timecode_nmarks = 2 + (range / fr);
736 } else if (range <= 60 * fr) { /* 30-60 seconds */
737 timecode_ruler_scale = timecode_show_seconds;
738 timecode_mark_modulo = 5;
739 timecode_nmarks = 2 + (range / fr);
740 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
741 timecode_ruler_scale = timecode_show_seconds;
742 timecode_mark_modulo = 15;
743 timecode_nmarks = 2 + (range / fr);
744 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
745 timecode_ruler_scale = timecode_show_seconds;
746 timecode_mark_modulo = 30;
747 timecode_nmarks = 2 + (range / fr);
748 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
749 timecode_ruler_scale = timecode_show_minutes;
750 timecode_mark_modulo = 2;
751 timecode_nmarks = 2 + 10;
752 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
753 timecode_ruler_scale = timecode_show_minutes;
754 timecode_mark_modulo = 5;
755 timecode_nmarks = 2 + 30;
756 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
757 timecode_ruler_scale = timecode_show_minutes;
758 timecode_mark_modulo = 10;
759 timecode_nmarks = 2 + 60;
760 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
761 timecode_ruler_scale = timecode_show_minutes;
762 timecode_mark_modulo = 30;
763 timecode_nmarks = 2 + (60 * 4);
764 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
765 timecode_ruler_scale = timecode_show_hours;
766 timecode_mark_modulo = 1;
767 timecode_nmarks = 2 + 8;
768 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
769 timecode_ruler_scale = timecode_show_hours;
770 timecode_mark_modulo = 1;
771 timecode_nmarks = 2 + 24;
774 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
775 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
777 /* Normally we do not need to know anything about the width of the canvas
778 to set the ruler scale, because the caller has already determined
779 the width and set lower + upper arguments to this function to match that.
781 But in this case, where the range defined by lower and uppper can vary
782 substantially (basically anything from 24hrs+ to several billion years)
783 trying to decide which tick marks to show does require us to know
784 about the available width.
787 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
788 timecode_ruler_scale = timecode_show_many_hours;
789 timecode_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
794 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
798 Timecode::Time timecode;
801 ArdourCanvas::Ruler::Mark mark;
807 if (lower > (spacer = (samplecnt_t)(128 * Editor::get_current_zoom ()))) {
808 lower = lower - spacer;
813 pos = (samplecnt_t) floor (lower);
815 switch (timecode_ruler_scale) {
816 case timecode_show_bits:
817 // Find timecode time of this sample (pos) with subframe accuracy
818 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
819 for (n = 0; n < timecode_nmarks; n++) {
820 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
821 if ((timecode.subframes % timecode_mark_modulo) == 0) {
822 if (timecode.subframes == 0) {
823 mark.style = ArdourCanvas::Ruler::Mark::Major;
824 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
826 mark.style = ArdourCanvas::Ruler::Mark::Minor;
827 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
830 snprintf (buf, sizeof(buf)," ");
831 mark.style = ArdourCanvas::Ruler::Mark::Micro;
835 marks.push_back (mark);
836 // Increment subframes by one
837 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
841 case timecode_show_samples:
842 // Find timecode time of this sample (pos)
843 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
844 // Go to next whole sample down
845 Timecode::frames_floot( timecode );
846 for (n = 0; n < timecode_nmarks; n++) {
847 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
848 if ((timecode.frames % timecode_mark_modulo) == 0) {
849 if (timecode.frames == 0) {
850 mark.style = ArdourCanvas::Ruler::Mark::Major;
852 mark.style = ArdourCanvas::Ruler::Mark::Minor;
855 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
857 snprintf (buf, sizeof(buf)," ");
858 mark.style = ArdourCanvas::Ruler::Mark::Micro;
862 marks.push_back (mark);
863 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
867 case timecode_show_seconds:
868 // Find timecode time of this sample (pos)
869 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
870 // Go to next whole second down
871 Timecode::seconds_floor( timecode );
872 for (n = 0; n < timecode_nmarks; n++) {
873 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
874 if ((timecode.seconds % timecode_mark_modulo) == 0) {
875 if (timecode.seconds == 0) {
876 mark.style = ArdourCanvas::Ruler::Mark::Major;
879 mark.style = ArdourCanvas::Ruler::Mark::Minor;
882 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
884 snprintf (buf, sizeof(buf)," ");
885 mark.style = ArdourCanvas::Ruler::Mark::Micro;
889 marks.push_back (mark);
890 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
894 case timecode_show_minutes:
895 //Find timecode time of this sample (pos)
896 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
897 // Go to next whole minute down
898 Timecode::minutes_floor( timecode );
899 for (n = 0; n < timecode_nmarks; n++) {
900 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
901 if ((timecode.minutes % timecode_mark_modulo) == 0) {
902 if (timecode.minutes == 0) {
903 mark.style = ArdourCanvas::Ruler::Mark::Major;
905 mark.style = ArdourCanvas::Ruler::Mark::Minor;
907 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
909 snprintf (buf, sizeof(buf)," ");
910 mark.style = ArdourCanvas::Ruler::Mark::Micro;
914 marks.push_back (mark);
915 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
918 case timecode_show_hours:
919 // Find timecode time of this sample (pos)
920 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
921 // Go to next whole hour down
922 Timecode::hours_floor( timecode );
923 for (n = 0; n < timecode_nmarks; n++) {
924 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
925 if ((timecode.hours % timecode_mark_modulo) == 0) {
926 mark.style = ArdourCanvas::Ruler::Mark::Major;
927 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
929 snprintf (buf, sizeof(buf)," ");
930 mark.style = ArdourCanvas::Ruler::Mark::Micro;
934 marks.push_back (mark);
935 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
938 case timecode_show_many_hours:
939 // Find timecode time of this sample (pos)
940 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
941 // Go to next whole hour down
942 Timecode::hours_floor (timecode);
944 for (n = 0; n < timecode_nmarks; ) {
945 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
946 if ((timecode.hours % timecode_mark_modulo) == 0) {
947 mark.style = ArdourCanvas::Ruler::Mark::Major;
948 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
951 marks.push_back (mark);
954 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
955 * and doing it 1 hour at a time is just stupid (and slow).
957 timecode.hours += timecode_mark_modulo - (timecode.hours % timecode_mark_modulo);
964 Editor::compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper)
970 std::vector<TempoMap::BBTPoint>::const_iterator i;
971 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
972 double floor_lower_beat = floor(max (0.0, _session->tempo_map().beat_at_sample (lower)));
974 if (floor_lower_beat < 0.0) {
975 floor_lower_beat = 0.0;
978 const samplecnt_t beat_before_lower_pos = _session->tempo_map().sample_at_beat (floor_lower_beat);
979 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);
981 _session->bbt_time (beat_before_lower_pos, lower_beat);
982 _session->bbt_time (beat_after_upper_pos, upper_beat);
985 bbt_accent_modulo = 1;
986 bbt_bar_helper_on = false;
990 bbt_ruler_scale = bbt_show_many;
992 switch (_grid_type) {
993 case GridTypeBeatDiv2:
994 bbt_beat_subdivision = 2;
996 case GridTypeBeatDiv3:
997 bbt_beat_subdivision = 3;
999 case GridTypeBeatDiv4:
1000 bbt_beat_subdivision = 4;
1002 case GridTypeBeatDiv5:
1003 bbt_beat_subdivision = 5;
1004 bbt_accent_modulo = 2; // XXX YIKES
1006 case GridTypeBeatDiv6:
1007 bbt_beat_subdivision = 6;
1008 bbt_accent_modulo = 2; // XXX YIKES
1010 case GridTypeBeatDiv7:
1011 bbt_beat_subdivision = 7;
1012 bbt_accent_modulo = 2; // XXX YIKES
1014 case GridTypeBeatDiv8:
1015 bbt_beat_subdivision = 8;
1016 bbt_accent_modulo = 2;
1018 case GridTypeBeatDiv10:
1019 bbt_beat_subdivision = 10;
1020 bbt_accent_modulo = 2; // XXX YIKES
1022 case GridTypeBeatDiv12:
1023 bbt_beat_subdivision = 12;
1024 bbt_accent_modulo = 3;
1026 case GridTypeBeatDiv14:
1027 bbt_beat_subdivision = 14;
1028 bbt_accent_modulo = 3; // XXX YIKES!
1030 case GridTypeBeatDiv16:
1031 bbt_beat_subdivision = 16;
1032 bbt_accent_modulo = 4;
1034 case GridTypeBeatDiv20:
1035 bbt_beat_subdivision = 20;
1036 bbt_accent_modulo = 5;
1038 case GridTypeBeatDiv24:
1039 bbt_beat_subdivision = 24;
1040 bbt_accent_modulo = 6;
1042 case GridTypeBeatDiv28:
1043 bbt_beat_subdivision = 28;
1044 bbt_accent_modulo = 7;
1046 case GridTypeBeatDiv32:
1047 bbt_beat_subdivision = 32;
1048 bbt_accent_modulo = 8;
1052 bbt_beat_subdivision = 4;
1056 case GridTypeMinSec:
1057 case GridTypeSamples:
1058 bbt_beat_subdivision = 4;
1062 const double ceil_upper_beat = floor (max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0;
1063 if (ceil_upper_beat == floor_lower_beat) {
1067 bbt_bars = _session->tempo_map().bbt_at_beat (ceil_upper_beat).bars - _session->tempo_map().bbt_at_beat (floor_lower_beat).bars;
1069 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
1070 double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / 5.0;
1072 /* Only show the bar helper if there aren't many bars on the screen */
1073 if ((bbt_bars < 2) || (beats < 5)) {
1074 bbt_bar_helper_on = true;
1077 //set upper limits on the beat_density based on the user's grid selection
1078 if ( _grid_type == GridTypeBar ) {
1079 beat_density = fmax (beat_density, 16.01);
1080 } else if ( _grid_type == GridTypeBeat ) {
1081 beat_density = fmax (beat_density, 4.001);
1082 } else if ( _grid_type == GridTypeBeatDiv4) {
1083 beat_density = fmax (beat_density, 2.001);
1084 } else if ( _grid_type == GridTypeBeatDiv8) {
1085 beat_density = fmax (beat_density, 1.001);
1086 } else if ( _grid_type == GridTypeBeatDiv16) {
1087 beat_density = fmax (beat_density, 0.2501);
1088 } else if ( _grid_type == GridTypeBeatDiv32) {
1089 beat_density = fmax (beat_density, 0.12501);
1092 if (beat_density > 8192) {
1093 bbt_ruler_scale = bbt_show_many;
1094 } else if (beat_density > 1024) {
1095 bbt_ruler_scale = bbt_show_64;
1096 } else if (beat_density > 512) {
1097 bbt_ruler_scale = bbt_show_16;
1098 } else if (beat_density > 128) {
1099 bbt_ruler_scale = bbt_show_4;
1100 } else if (beat_density > 16) {
1101 bbt_ruler_scale = bbt_show_1;
1102 } else if (beat_density > 4) {
1103 bbt_ruler_scale = bbt_show_beats;
1104 } else if (beat_density > 1) {
1105 bbt_ruler_scale = bbt_show_ticks;
1106 } else if (beat_density > 0.25) {
1107 bbt_ruler_scale = bbt_show_ticks_detail;
1109 bbt_ruler_scale = bbt_show_ticks_super_detail;
1114 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1116 ArdourCanvas::Ruler::Mark copy = marks.back();
1117 copy.label = newlabel;
1119 marks.push_back (copy);
1123 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1125 if (_session == 0) {
1129 std::vector<TempoMap::BBTPoint>::const_iterator i;
1134 Timecode::BBT_Time next_beat;
1139 double bbt_position_of_helper;
1140 bool i_am_accented = false;
1141 bool helper_active = false;
1142 ArdourCanvas::Ruler::Mark mark;
1144 std::vector<TempoMap::BBTPoint> grid;
1146 compute_current_bbt_points (grid, lower, upper);
1148 if (distance (grid.begin(), grid.end()) == 0) {
1152 switch (bbt_ruler_scale) {
1154 case bbt_show_beats:
1156 beats = distance (grid.begin(), grid.end());
1157 bbt_nmarks = beats + 2;
1160 mark.position = lower;
1161 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1162 marks.push_back (mark);
1164 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1166 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1167 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1168 edit_last_mark_label (marks, buf);
1171 if ((*i).is_bar()) {
1172 mark.style = ArdourCanvas::Ruler::Mark::Major;
1173 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1174 } else if (((*i).beat % 2 == 1)) {
1175 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1178 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1182 mark.position = (*i).sample;
1183 marks.push_back (mark);
1189 case bbt_show_ticks:
1191 beats = distance (grid.begin(), grid.end());
1192 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1194 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1196 // could do marks.assign() here to preallocate
1199 mark.position = lower;
1200 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1201 marks.push_back (mark);
1203 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1205 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1206 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1207 edit_last_mark_label (marks, buf);
1208 helper_active = true;
1211 if ((*i).is_bar()) {
1212 mark.style = ArdourCanvas::Ruler::Mark::Major;
1213 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1215 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1216 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1218 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1222 mark.position = (*i).sample;
1223 marks.push_back (mark);
1227 /* Add the tick marks */
1228 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1229 tick = skip; // the first non-beat tick
1231 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1233 next_beat.beats = (*i).beat;
1234 next_beat.bars = (*i).bar;
1235 next_beat.ticks = tick;
1236 pos = _session->tempo_map().sample_at_bbt (next_beat);
1238 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1239 i_am_accented = true;
1242 mark.position = pos;
1244 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1245 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1247 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1249 i_am_accented = false;
1250 marks.push_back (mark);
1260 case bbt_show_ticks_detail:
1262 beats = distance (grid.begin(), grid.end());
1263 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1265 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1268 mark.position = lower;
1269 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1270 marks.push_back (mark);
1272 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1274 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1275 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1276 edit_last_mark_label (marks, buf);
1277 helper_active = true;
1280 if ((*i).is_bar()) {
1281 mark.style = ArdourCanvas::Ruler::Mark::Major;
1282 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1284 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1285 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1287 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1291 mark.position = (*i).sample;
1292 marks.push_back (mark);
1296 /* Add the tick marks */
1297 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1298 tick = skip; // the first non-beat tick
1301 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1303 next_beat.beats = (*i).beat;
1304 next_beat.bars = (*i).bar;
1305 next_beat.ticks = tick;
1306 pos = _session->tempo_map().sample_at_bbt (next_beat);
1308 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1309 i_am_accented = true;
1311 if (i_am_accented && (pos > bbt_position_of_helper)){
1312 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1318 mark.position = pos;
1320 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1321 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1323 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1325 i_am_accented = false;
1326 marks.push_back (mark);
1336 case bbt_show_ticks_super_detail:
1338 beats = distance (grid.begin(), grid.end());
1339 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1341 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1344 mark.position = lower;
1345 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1346 marks.push_back (mark);
1348 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1350 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1351 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1352 edit_last_mark_label (marks, buf);
1353 helper_active = true;
1356 if ((*i).is_bar()) {
1357 mark.style = ArdourCanvas::Ruler::Mark::Major;
1358 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1360 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1361 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1363 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1367 mark.position = (*i).sample;
1368 marks.push_back (mark);
1372 /* Add the tick marks */
1373 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1375 next_beat.beats = (*i).beat;
1376 next_beat.bars = (*i).bar;
1377 tick = skip; // the first non-beat tick
1379 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1381 next_beat.ticks = tick;
1382 pos = _session->tempo_map().sample_at_bbt (next_beat);
1383 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1384 i_am_accented = true;
1387 if (pos > bbt_position_of_helper) {
1388 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1394 mark.position = pos;
1396 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1397 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1399 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1401 i_am_accented = false;
1402 marks.push_back (mark);
1414 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1415 mark.style = ArdourCanvas::Ruler::Mark::Major;
1417 mark.position = lower;
1418 marks.push_back (mark);
1422 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1423 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1424 if ((*i).is_bar()) {
1425 if ((*i).bar % 64 == 1) {
1426 if ((*i).bar % 256 == 1) {
1427 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1428 mark.style = ArdourCanvas::Ruler::Mark::Major;
1431 if ((*i).bar % 256 == 129) {
1432 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1434 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1438 mark.position = (*i).sample;
1439 marks.push_back (mark);
1447 bbt_nmarks = (bbt_bars / 16) + 1;
1448 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1449 if ((*i).is_bar()) {
1450 if ((*i).bar % 16 == 1) {
1451 if ((*i).bar % 64 == 1) {
1452 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1453 mark.style = ArdourCanvas::Ruler::Mark::Major;
1456 if ((*i).bar % 64 == 33) {
1457 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1459 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1463 mark.position = (*i).sample;
1464 marks.push_back (mark);
1472 bbt_nmarks = (bbt_bars / 4) + 1;
1473 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1474 if ((*i).is_bar()) {
1475 if ((*i).bar % 4 == 1) {
1476 if ((*i).bar % 16 == 1) {
1477 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1478 mark.style = ArdourCanvas::Ruler::Mark::Major;
1481 if ((*i).bar % 16 == 9) {
1482 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1484 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1488 mark.position = (*i).sample;
1489 marks.push_back (mark);
1498 bbt_nmarks = bbt_bars + 2;
1499 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1500 if ((*i).is_bar()) {
1501 if ((*i).bar % 4 == 1) {
1502 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1503 mark.style = ArdourCanvas::Ruler::Mark::Major;
1506 if ((*i).bar % 4 == 3) {
1507 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1509 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1513 mark.position = (*i).sample;
1514 marks.push_back (mark);
1524 Editor::set_samples_ruler_scale (samplepos_t lower, samplepos_t upper)
1526 _samples_ruler_interval = (upper - lower) / 5;
1530 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1533 samplepos_t const ilower = (samplepos_t) floor (lower);
1537 ArdourCanvas::Ruler::Mark mark;
1539 if (_session == 0) {
1544 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1545 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1547 mark.position = pos;
1548 mark.style = ArdourCanvas::Ruler::Mark::Major;
1549 marks.push_back (mark);
1554 sample_to_clock_parts ( samplepos_t sample,
1555 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;