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_tempo (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 (_snap_type) {
994 bbt_beat_subdivision = 2;
997 bbt_beat_subdivision = 3;
1000 bbt_beat_subdivision = 4;
1002 case SnapToBeatDiv5:
1003 bbt_beat_subdivision = 5;
1004 bbt_accent_modulo = 2; // XXX YIKES
1006 case SnapToBeatDiv6:
1007 bbt_beat_subdivision = 6;
1008 bbt_accent_modulo = 2; // XXX YIKES
1010 case SnapToBeatDiv7:
1011 bbt_beat_subdivision = 7;
1012 bbt_accent_modulo = 2; // XXX YIKES
1014 case SnapToBeatDiv8:
1015 bbt_beat_subdivision = 8;
1016 bbt_accent_modulo = 2;
1018 case SnapToBeatDiv10:
1019 bbt_beat_subdivision = 10;
1020 bbt_accent_modulo = 2; // XXX YIKES
1022 case SnapToBeatDiv12:
1023 bbt_beat_subdivision = 12;
1024 bbt_accent_modulo = 3;
1026 case SnapToBeatDiv14:
1027 bbt_beat_subdivision = 14;
1028 bbt_accent_modulo = 3; // XXX YIKES!
1030 case SnapToBeatDiv16:
1031 bbt_beat_subdivision = 16;
1032 bbt_accent_modulo = 4;
1034 case SnapToBeatDiv20:
1035 bbt_beat_subdivision = 20;
1036 bbt_accent_modulo = 5;
1038 case SnapToBeatDiv24:
1039 bbt_beat_subdivision = 24;
1040 bbt_accent_modulo = 6;
1042 case SnapToBeatDiv28:
1043 bbt_beat_subdivision = 28;
1044 bbt_accent_modulo = 7;
1046 case SnapToBeatDiv32:
1047 bbt_beat_subdivision = 32;
1048 bbt_accent_modulo = 8;
1050 case SnapToBeatDiv64:
1051 bbt_beat_subdivision = 64;
1052 bbt_accent_modulo = 8;
1054 case SnapToBeatDiv128:
1055 bbt_beat_subdivision = 128;
1056 bbt_accent_modulo = 8;
1059 bbt_beat_subdivision = 4;
1063 const double ceil_upper_beat = floor (max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0;
1064 if (ceil_upper_beat == floor_lower_beat) {
1068 bbt_bars = _session->tempo_map().bbt_at_beat (ceil_upper_beat).bars - _session->tempo_map().bbt_at_beat (floor_lower_beat).bars;
1070 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
1071 double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / 5.0;
1073 /* Only show the bar helper if there aren't many bars on the screen */
1074 if ((bbt_bars < 2) || (beats < 5)) {
1075 bbt_bar_helper_on = true;
1078 if (beat_density > 8192) {
1079 bbt_ruler_scale = bbt_show_many;
1080 } else if (beat_density > 1024) {
1081 bbt_ruler_scale = bbt_show_64;
1082 } else if (beat_density > 512) {
1083 bbt_ruler_scale = bbt_show_16;
1084 } else if (beat_density > 128) {
1085 bbt_ruler_scale = bbt_show_4;
1086 } else if (beat_density > 16) {
1087 bbt_ruler_scale = bbt_show_1;
1088 } else if (beat_density > 2) {
1089 bbt_ruler_scale = bbt_show_beats;
1090 } else if (beat_density > 0.5) {
1091 bbt_ruler_scale = bbt_show_ticks;
1093 bbt_ruler_scale = bbt_show_ticks_detail;
1096 if ((bbt_ruler_scale == bbt_show_ticks_detail) && beats < 3) {
1097 bbt_ruler_scale = bbt_show_ticks_super_detail;
1102 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1104 ArdourCanvas::Ruler::Mark copy = marks.back();
1105 copy.label = newlabel;
1107 marks.push_back (copy);
1111 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1113 if (_session == 0) {
1117 std::vector<TempoMap::BBTPoint>::const_iterator i;
1122 Timecode::BBT_Time next_beat;
1127 double bbt_position_of_helper;
1128 bool i_am_accented = false;
1129 bool helper_active = false;
1130 ArdourCanvas::Ruler::Mark mark;
1132 std::vector<TempoMap::BBTPoint> grid;
1134 compute_current_bbt_points (grid, lower, upper);
1136 if (distance (grid.begin(), grid.end()) == 0) {
1140 switch (bbt_ruler_scale) {
1142 case bbt_show_beats:
1144 beats = distance (grid.begin(), grid.end());
1145 bbt_nmarks = beats + 2;
1148 mark.position = lower;
1149 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1150 marks.push_back (mark);
1152 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1154 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1155 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1156 edit_last_mark_label (marks, buf);
1159 if ((*i).is_bar()) {
1160 mark.style = ArdourCanvas::Ruler::Mark::Major;
1161 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1162 } else if (((*i).beat % 2 == 1)) {
1163 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1166 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1170 mark.position = (*i).sample;
1171 marks.push_back (mark);
1177 case bbt_show_ticks:
1179 beats = distance (grid.begin(), grid.end());
1180 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1182 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1184 // could do marks.assign() here to preallocate
1187 mark.position = lower;
1188 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1189 marks.push_back (mark);
1191 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1193 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1194 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1195 edit_last_mark_label (marks, buf);
1196 helper_active = true;
1199 if ((*i).is_bar()) {
1200 mark.style = ArdourCanvas::Ruler::Mark::Major;
1201 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1203 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1204 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1206 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1210 mark.position = (*i).sample;
1211 marks.push_back (mark);
1215 /* Add the tick marks */
1216 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1217 tick = skip; // the first non-beat tick
1219 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1221 next_beat.beats = (*i).beat;
1222 next_beat.bars = (*i).bar;
1223 next_beat.ticks = tick;
1224 pos = _session->tempo_map().sample_at_bbt (next_beat);
1226 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1227 i_am_accented = true;
1230 mark.position = pos;
1232 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1233 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1235 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1237 i_am_accented = false;
1238 marks.push_back (mark);
1248 case bbt_show_ticks_detail:
1250 beats = distance (grid.begin(), grid.end());
1251 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1253 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1256 mark.position = lower;
1257 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1258 marks.push_back (mark);
1260 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1262 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1263 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1264 edit_last_mark_label (marks, buf);
1265 helper_active = true;
1268 if ((*i).is_bar()) {
1269 mark.style = ArdourCanvas::Ruler::Mark::Major;
1270 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1272 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1273 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1275 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1279 mark.position = (*i).sample;
1280 marks.push_back (mark);
1284 /* Add the tick marks */
1285 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1286 tick = skip; // the first non-beat tick
1289 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1291 next_beat.beats = (*i).beat;
1292 next_beat.bars = (*i).bar;
1293 next_beat.ticks = tick;
1294 pos = _session->tempo_map().sample_at_bbt (next_beat);
1296 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1297 i_am_accented = true;
1299 if (i_am_accented && (pos > bbt_position_of_helper)){
1300 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1306 mark.position = pos;
1308 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1309 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1311 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1313 i_am_accented = false;
1314 marks.push_back (mark);
1324 case bbt_show_ticks_super_detail:
1326 beats = distance (grid.begin(), grid.end());
1327 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1329 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1332 mark.position = lower;
1333 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1334 marks.push_back (mark);
1336 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1338 if ((*i).sample < lower && (bbt_bar_helper_on)) {
1339 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1340 edit_last_mark_label (marks, buf);
1341 helper_active = true;
1344 if ((*i).is_bar()) {
1345 mark.style = ArdourCanvas::Ruler::Mark::Major;
1346 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1348 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1349 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1351 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1355 mark.position = (*i).sample;
1356 marks.push_back (mark);
1360 /* Add the tick marks */
1361 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1363 next_beat.beats = (*i).beat;
1364 next_beat.bars = (*i).bar;
1365 tick = skip; // the first non-beat tick
1367 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1369 next_beat.ticks = tick;
1370 pos = _session->tempo_map().sample_at_bbt (next_beat);
1371 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1372 i_am_accented = true;
1375 if (pos > bbt_position_of_helper) {
1376 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1382 mark.position = pos;
1384 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1385 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1387 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1389 i_am_accented = false;
1390 marks.push_back (mark);
1402 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1403 mark.style = ArdourCanvas::Ruler::Mark::Major;
1405 mark.position = lower;
1406 marks.push_back (mark);
1410 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1411 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1412 if ((*i).is_bar()) {
1413 if ((*i).bar % 64 == 1) {
1414 if ((*i).bar % 256 == 1) {
1415 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1416 mark.style = ArdourCanvas::Ruler::Mark::Major;
1419 if ((*i).bar % 256 == 129) {
1420 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1422 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1426 mark.position = (*i).sample;
1427 marks.push_back (mark);
1435 bbt_nmarks = (bbt_bars / 16) + 1;
1436 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1437 if ((*i).is_bar()) {
1438 if ((*i).bar % 16 == 1) {
1439 if ((*i).bar % 64 == 1) {
1440 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1441 mark.style = ArdourCanvas::Ruler::Mark::Major;
1444 if ((*i).bar % 64 == 33) {
1445 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1447 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1451 mark.position = (*i).sample;
1452 marks.push_back (mark);
1460 bbt_nmarks = (bbt_bars / 4) + 1;
1461 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1462 if ((*i).is_bar()) {
1463 if ((*i).bar % 4 == 1) {
1464 if ((*i).bar % 16 == 1) {
1465 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1466 mark.style = ArdourCanvas::Ruler::Mark::Major;
1469 if ((*i).bar % 16 == 9) {
1470 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1472 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1476 mark.position = (*i).sample;
1477 marks.push_back (mark);
1486 bbt_nmarks = bbt_bars + 2;
1487 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1488 if ((*i).is_bar()) {
1489 if ((*i).bar % 4 == 1) {
1490 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1491 mark.style = ArdourCanvas::Ruler::Mark::Major;
1494 if ((*i).bar % 4 == 3) {
1495 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1497 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1501 mark.position = (*i).sample;
1502 marks.push_back (mark);
1512 Editor::set_samples_ruler_scale (samplepos_t lower, samplepos_t upper)
1514 _samples_ruler_interval = (upper - lower) / 5;
1518 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1521 samplepos_t const ilower = (samplepos_t) floor (lower);
1525 ArdourCanvas::Ruler::Mark mark;
1527 if (_session == 0) {
1532 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1533 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1535 mark.position = pos;
1536 mark.style = ArdourCanvas::Ruler::Mark::Major;
1537 marks.push_back (mark);
1542 sample_to_clock_parts ( samplepos_t sample,
1543 samplepos_t sample_rate,
1557 hrs = left / (sample_rate * 60 * 60 * 1000);
1558 left -= hrs * sample_rate * 60 * 60 * 1000;
1559 mins = left / (sample_rate * 60 * 1000);
1560 left -= mins * sample_rate * 60 * 1000;
1561 secs = left / (sample_rate * 1000);
1562 left -= secs * sample_rate * 1000;
1563 millisecs = left / sample_rate;
1565 *millisecs_p = millisecs;
1574 Editor::set_minsec_ruler_scale (samplepos_t lower, samplepos_t upper)
1576 samplepos_t fr = _session->sample_rate() * 1000;
1579 if (_session == 0) {
1584 /* to prevent 'flashing' */
1585 if (lower > (spacer = (samplepos_t)(128 * Editor::get_current_zoom ()))) {
1591 samplecnt_t const range = (upper - lower) * 1000;
1593 if (range <= (fr / 10)) { /* 0-0.1 second */
1594 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1595 minsec_ruler_scale = minsec_show_msecs;
1596 minsec_mark_modulo = 10;
1597 minsec_nmarks = 2 + (range / minsec_mark_interval);
1598 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1599 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1600 minsec_ruler_scale = minsec_show_msecs;
1601 minsec_mark_modulo = 100;
1602 minsec_nmarks = 2 + (range / minsec_mark_interval);
1603 } else if (range <= fr) { /* 0-1 second */
1604 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1605 minsec_ruler_scale = minsec_show_msecs;
1606 minsec_mark_modulo = 200;
1607 minsec_nmarks = 2 + (range / minsec_mark_interval);
1608 } else if (range <= 2 * fr) { /* 1-2 seconds */
1609 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1610 minsec_ruler_scale = minsec_show_msecs;
1611 minsec_mark_modulo = 500;
1612 minsec_nmarks = 2 + (range / minsec_mark_interval);
1613 } else if (range <= 8 * fr) { /* 2-5 seconds */
1614 minsec_mark_interval = fr / 5; /* show 2 seconds */
1615 minsec_ruler_scale = minsec_show_msecs;
1616 minsec_mark_modulo = 1000;
1617 minsec_nmarks = 2 + (range / minsec_mark_interval);
1618 } else if (range <= 16 * fr) { /* 8-16 seconds */
1619 minsec_mark_interval = fr; /* show 1 seconds */
1620 minsec_ruler_scale = minsec_show_seconds;
1621 minsec_mark_modulo = 2;
1622 minsec_nmarks = 2 + (range / minsec_mark_interval);
1623 } else if (range <= 30 * fr) { /* 10-30 seconds */
1624 minsec_mark_interval = fr; /* show 1 seconds */
1625 minsec_ruler_scale = minsec_show_seconds;
1626 minsec_mark_modulo = 5;
1627 minsec_nmarks = 2 + (range / minsec_mark_interval);
1628 } else if (range <= 60 * fr) { /* 30-60 seconds */
1629 minsec_mark_interval = fr; /* show 1 seconds */
1630 minsec_ruler_scale = minsec_show_seconds;
1631 minsec_mark_modulo = 5;
1632 minsec_nmarks = 2 + (range / minsec_mark_interval);
1633 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1634 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1635 minsec_ruler_scale = minsec_show_seconds;
1636 minsec_mark_modulo = 3;
1637 minsec_nmarks = 2 + (range / minsec_mark_interval);
1638 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1639 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1640 minsec_ruler_scale = minsec_show_seconds;
1641 minsec_mark_modulo = 30;
1642 minsec_nmarks = 2 + (range / minsec_mark_interval);
1643 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1644 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1645 minsec_ruler_scale = minsec_show_seconds;
1646 minsec_mark_modulo = 120;
1647 minsec_nmarks = 2 + (range / minsec_mark_interval);
1648 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1649 minsec_mark_interval = 60 * fr; /* show 1 minute */
1650 minsec_ruler_scale = minsec_show_minutes;
1651 minsec_mark_modulo = 5;
1652 minsec_nmarks = 2 + (range / minsec_mark_interval);
1653 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1654 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1655 minsec_ruler_scale = minsec_show_minutes;
1656 minsec_mark_modulo = 10;
1657 minsec_nmarks = 2 + (range / minsec_mark_interval);
1658 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1659 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1660 minsec_ruler_scale = minsec_show_minutes;
1661 minsec_mark_modulo = 30;
1662 minsec_nmarks = 2 + (range / minsec_mark_interval);
1663 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1664 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1665 minsec_ruler_scale = minsec_show_minutes;
1666 minsec_mark_modulo = 60;
1667 minsec_nmarks = 2 + (range / minsec_mark_interval);
1668 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1669 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1670 minsec_ruler_scale = minsec_show_hours;
1671 minsec_mark_modulo = 2;
1672 minsec_nmarks = 2 + (range / minsec_mark_interval);
1675 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
1676 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1678 /* Normally we do not need to know anything about the width of the canvas
1679 to set the ruler scale, because the caller has already determined
1680 the width and set lower + upper arguments to this function to match that.
1682 But in this case, where the range defined by lower and uppper can vary
1683 substantially (anything from 24hrs+ to several billion years)
1684 trying to decide which tick marks to show does require us to know
1685 about the available width.
1688 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1689 minsec_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1690 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1691 minsec_ruler_scale = minsec_show_many_hours;
1696 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1700 long hrs, mins, secs, millisecs;
1703 ArdourCanvas::Ruler::Mark mark;
1705 if (_session == 0) {
1709 /* to prevent 'flashing' */
1710 if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
1711 lower = lower - spacer;
1716 pos = (((1000 * (samplepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1718 switch (minsec_ruler_scale) {
1720 case minsec_show_msecs:
1721 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1722 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1723 if (millisecs % minsec_mark_modulo == 0) {
1724 if (millisecs == 0) {
1725 mark.style = ArdourCanvas::Ruler::Mark::Major;
1727 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1729 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1732 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1735 mark.position = pos/1000.0;
1736 marks.push_back (mark);
1740 case minsec_show_seconds:
1741 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1742 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1743 if (secs % minsec_mark_modulo == 0) {
1745 mark.style = ArdourCanvas::Ruler::Mark::Major;
1747 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1749 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1752 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1755 mark.position = pos/1000.0;
1756 marks.push_back (mark);
1760 case minsec_show_minutes:
1761 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1762 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1763 if (mins % minsec_mark_modulo == 0) {
1765 mark.style = ArdourCanvas::Ruler::Mark::Major;
1767 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1769 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1772 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1775 mark.position = pos/1000.0;
1776 marks.push_back (mark);
1780 case minsec_show_hours:
1781 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1782 sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1783 if (hrs % minsec_mark_modulo == 0) {
1784 mark.style = ArdourCanvas::Ruler::Mark::Major;
1785 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1788 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1791 mark.position = pos/1000.0;
1792 marks.push_back (mark);
1796 case minsec_show_many_hours:
1797 for (n = 0; n < minsec_nmarks; ) {
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:00", hrs);
1803 mark.position = pos/1000.0;
1804 marks.push_back (mark);
1807 pos += minsec_mark_interval;