2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
24 #include <cstdio> // for sprintf, grrr
30 #include <gtk/gtkaction.h>
32 #include "canvas/container.h"
33 #include "canvas/canvas.h"
34 #include "canvas/ruler.h"
35 #include "canvas/debug.h"
36 #include "canvas/scroll_group.h"
38 #include "ardour/session.h"
39 #include "ardour/tempo.h"
40 #include "ardour/profile.h"
42 #include "gtkmm2ext/gtk_ui.h"
43 #include "gtkmm2ext/keyboard.h"
45 #include "ardour_ui.h"
49 #include "gui_thread.h"
50 #include "ruler_dialog.h"
51 #include "time_axis_view.h"
52 #include "editor_drag.h"
53 #include "editor_cursors.h"
54 #include "ui_config.h"
58 using namespace ARDOUR;
61 using namespace Editing;
63 /* the order here must match the "metric" enums in editor.h */
65 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
68 TimecodeMetric (Editor* e) : _editor (e) {}
70 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
71 _editor->metric_get_timecode (marks, lower, upper, maxchars);
78 class SamplesMetric : public ArdourCanvas::Ruler::Metric
81 SamplesMetric (Editor* e) : _editor (e) {}
83 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
84 _editor->metric_get_samples (marks, lower, upper, maxchars);
91 class BBTMetric : public ArdourCanvas::Ruler::Metric
94 BBTMetric (Editor* e) : _editor (e) {}
96 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
97 _editor->metric_get_bbt (marks, lower, upper, maxchars);
104 class MinsecMetric : public ArdourCanvas::Ruler::Metric
107 MinsecMetric (Editor* e) : _editor (e) {}
109 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
110 _editor->metric_get_minsec (marks, lower, upper, maxchars);
117 static ArdourCanvas::Ruler::Metric* _bbt_metric;
118 static ArdourCanvas::Ruler::Metric* _timecode_metric;
119 static ArdourCanvas::Ruler::Metric* _samples_metric;
120 static ArdourCanvas::Ruler::Metric* _minsec_metric;
123 Editor::initialize_rulers ()
125 ruler_grabbed_widget = 0;
127 Pango::FontDescription font (UIConfiguration::instance().get_SmallerFont());
129 _timecode_metric = new TimecodeMetric (this);
130 _bbt_metric = new BBTMetric (this);
131 _minsec_metric = new MinsecMetric (this);
132 _samples_metric = new SamplesMetric (this);
134 timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
135 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
136 timecode_ruler->set_font_description (font);
137 CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
140 samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
141 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
142 samples_ruler->set_font_description (font);
143 CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
145 minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
146 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
147 minsec_ruler->set_font_description (font);
148 CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
151 bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
152 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
153 bbt_ruler->set_font_description (font);
154 CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
157 using namespace Box_Helpers;
158 BoxList & lab_children = time_bars_vbox.children();
160 lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
161 lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
162 lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
163 lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
164 lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
165 lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
166 lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
167 lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
168 lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
169 lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
170 lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
172 /* 1 event handler to bind them all ... */
174 timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
175 minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
176 bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
177 samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
179 visible_timebars = 0; /*this will be changed below */
183 Editor::ruler_label_button_release (GdkEventButton* ev)
185 if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
187 ruler_dialog = new RulerDialog ();
189 ruler_dialog->present ();
196 Editor::popup_ruler_menu (framepos_t where, ItemType t)
198 using namespace Menu_Helpers;
200 if (editor_ruler_menu == 0) {
201 editor_ruler_menu = new Menu;
202 editor_ruler_menu->set_name ("ArdourContextMenu");
205 // always build from scratch
206 MenuList& ruler_items = editor_ruler_menu->items();
207 editor_ruler_menu->set_name ("ArdourContextMenu");
212 ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false)));
213 ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
214 ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
217 case RangeMarkerBarItem:
218 ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
219 ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
220 ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
223 case TransportMarkerBarItem:
224 ruler_items.push_back (MenuElem (_("New Loop range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_loop), where)));
225 ruler_items.push_back (MenuElem (_("New Punch range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_punch), where)));
228 case CdMarkerBarItem:
230 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true)));
234 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
238 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
242 /* proper headings would be nice
243 * but AFAICT the only way to get them will be to define a
244 * special GTK style for insensitive Elements or subclass MenuItem
246 //ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
247 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
248 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
249 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
250 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
251 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
252 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
253 if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
255 ruler_items.push_back (SeparatorElem ());
257 //ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
258 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
259 ruler_items.push_back (CheckMenuElem (_("Lock")));
261 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
262 vtl_lock->set_active(is_video_timeline_locked());
263 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
266 ruler_items.push_back (SeparatorElem ());
268 //ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
269 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
270 ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
272 Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
273 if (!ARDOUR_UI::instance()->video_timeline->found_xjadeo()) {
274 xjadeo_toggle->set_sensitive(false);
276 xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
277 xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
285 if (!ruler_items.empty()) {
286 editor_ruler_menu->popup (1, gtk_get_current_event_time());
289 no_ruler_shown_update = false;
293 Editor::store_ruler_visibility ()
295 XMLNode* node = new XMLNode(X_("RulerVisibility"));
297 node->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 framepos_t rightmost_frame = leftmost_frame + current_page_samples();
607 if (ruler_timecode_action->get_active()) {
608 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
613 Editor::compute_fixed_ruler_scale ()
619 if (ruler_timecode_action->get_active()) {
620 set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
623 if (ruler_minsec_action->get_active()) {
624 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
627 if (ruler_samples_action->get_active()) {
628 set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
633 Editor::update_fixed_rulers ()
635 framepos_t rightmost_frame;
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_frame = leftmost_frame + 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_frame, rightmost_frame);
657 if (ruler_samples_action->get_active()) {
658 samples_ruler->set_range (leftmost_frame, rightmost_frame);
661 if (ruler_minsec_action->get_active()) {
662 minsec_ruler->set_range (leftmost_frame, rightmost_frame);
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_frame, leftmost_frame+current_page_samples());
682 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
693 fr = _session->frame_rate();
695 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
696 lower = lower - spacer;
701 upper = upper + spacer;
702 framecnt_t const range = upper - lower;
704 if (range < (2 * _session->samples_per_timecode_frame())) { /* 0 - 2 frames */
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 frames - 0.250 second */
709 timecode_ruler_scale = timecode_show_frames;
710 timecode_mark_modulo = 1;
711 timecode_nmarks = 2 + (range / (framepos_t)_session->samples_per_timecode_frame());
712 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
713 timecode_ruler_scale = timecode_show_frames;
714 timecode_mark_modulo = 2;
715 timecode_nmarks = 2 + (range / (framepos_t)_session->samples_per_timecode_frame());
716 } else if (range <= fr) { /* 0.5-1 second */
717 timecode_ruler_scale = timecode_show_frames;
718 timecode_mark_modulo = 5;
719 timecode_nmarks = 2 + (range / (framepos_t)_session->samples_per_timecode_frame());
720 } else if (range <= 2 * fr) { /* 1-2 seconds */
721 timecode_ruler_scale = timecode_show_frames;
722 timecode_mark_modulo = 10;
723 timecode_nmarks = 2 + (range / (framepos_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 framecnt_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 ((framecnt_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 = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
808 lower = lower - spacer;
813 pos = (framecnt_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_frames:
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 frame down
845 Timecode::frames_floor( 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 (framepos_t lower, framepos_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_frame (lower)));
974 if (floor_lower_beat < 0.0) {
975 floor_lower_beat = 0.0;
978 const framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor_lower_beat);
979 const framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (max (0.0, _session->tempo_map().beat_at_frame (upper))) + 1.0);
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_frame (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).frame < lower && (bbt_bar_helper_on)) {
1155 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1156 edit_last_mark_label (marks, buf);
1157 helper_active = true;
1160 if ((*i).is_bar()) {
1161 mark.style = ArdourCanvas::Ruler::Mark::Major;
1162 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1163 } else if (((*i).beat % 2 == 1)) {
1164 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1167 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1171 mark.position = (*i).frame;
1172 marks.push_back (mark);
1178 case bbt_show_ticks:
1180 beats = distance (grid.begin(), grid.end());
1181 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1183 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1185 // could do marks.assign() here to preallocate
1188 mark.position = lower;
1189 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1190 marks.push_back (mark);
1192 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1194 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1195 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1196 edit_last_mark_label (marks, buf);
1197 helper_active = true;
1200 if ((*i).is_bar()) {
1201 mark.style = ArdourCanvas::Ruler::Mark::Major;
1202 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1204 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1205 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1207 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1211 mark.position = (*i).frame;
1212 marks.push_back (mark);
1216 /* Add the tick marks */
1217 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1218 tick = skip; // the first non-beat tick
1220 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1222 next_beat.beats = (*i).beat;
1223 next_beat.bars = (*i).bar;
1224 next_beat.ticks = tick;
1225 pos = _session->tempo_map().frame_at_bbt (next_beat);
1227 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1228 i_am_accented = true;
1231 mark.position = pos;
1233 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1234 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1236 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1238 i_am_accented = false;
1239 marks.push_back (mark);
1249 case bbt_show_ticks_detail:
1251 beats = distance (grid.begin(), grid.end());
1252 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1254 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1257 mark.position = lower;
1258 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1259 marks.push_back (mark);
1261 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1263 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1264 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1265 edit_last_mark_label (marks, buf);
1266 helper_active = true;
1269 if ((*i).is_bar()) {
1270 mark.style = ArdourCanvas::Ruler::Mark::Major;
1271 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1273 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1274 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1276 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1280 mark.position = (*i).frame;
1281 marks.push_back (mark);
1285 /* Add the tick marks */
1286 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1287 tick = skip; // the first non-beat tick
1290 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1292 next_beat.beats = (*i).beat;
1293 next_beat.bars = (*i).bar;
1294 next_beat.ticks = tick;
1295 pos = _session->tempo_map().frame_at_bbt (next_beat);
1297 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1298 i_am_accented = true;
1300 if (i_am_accented && (pos > bbt_position_of_helper)){
1301 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1307 mark.position = pos;
1309 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1310 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1312 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1314 i_am_accented = false;
1315 marks.push_back (mark);
1325 case bbt_show_ticks_super_detail:
1327 beats = distance (grid.begin(), grid.end());
1328 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1330 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1333 mark.position = lower;
1334 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1335 marks.push_back (mark);
1337 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1339 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1340 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1341 edit_last_mark_label (marks, buf);
1342 helper_active = true;
1345 if ((*i).is_bar()) {
1346 mark.style = ArdourCanvas::Ruler::Mark::Major;
1347 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1349 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1350 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1352 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1356 mark.position = (*i).frame;
1357 marks.push_back (mark);
1361 /* Add the tick marks */
1362 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1364 next_beat.beats = (*i).beat;
1365 next_beat.bars = (*i).bar;
1366 tick = skip; // the first non-beat tick
1368 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1370 next_beat.ticks = tick;
1371 pos = _session->tempo_map().frame_at_bbt (next_beat);
1372 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1373 i_am_accented = true;
1376 if (pos > bbt_position_of_helper) {
1377 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1383 mark.position = pos;
1385 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1386 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1388 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1390 i_am_accented = false;
1391 marks.push_back (mark);
1403 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1404 mark.style = ArdourCanvas::Ruler::Mark::Major;
1406 mark.position = lower;
1407 marks.push_back (mark);
1411 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1412 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1413 if ((*i).is_bar()) {
1414 if ((*i).bar % 64 == 1) {
1415 if ((*i).bar % 256 == 1) {
1416 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1417 mark.style = ArdourCanvas::Ruler::Mark::Major;
1420 if ((*i).bar % 256 == 129) {
1421 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1423 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1427 mark.position = (*i).frame;
1428 marks.push_back (mark);
1436 bbt_nmarks = (bbt_bars / 16) + 1;
1437 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1438 if ((*i).is_bar()) {
1439 if ((*i).bar % 16 == 1) {
1440 if ((*i).bar % 64 == 1) {
1441 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1442 mark.style = ArdourCanvas::Ruler::Mark::Major;
1445 if ((*i).bar % 64 == 33) {
1446 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1448 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1452 mark.position = (*i).frame;
1453 marks.push_back (mark);
1461 bbt_nmarks = (bbt_bars / 4) + 1;
1462 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1463 if ((*i).is_bar()) {
1464 if ((*i).bar % 4 == 1) {
1465 if ((*i).bar % 16 == 1) {
1466 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1467 mark.style = ArdourCanvas::Ruler::Mark::Major;
1470 if ((*i).bar % 16 == 9) {
1471 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1473 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1477 mark.position = (*i).frame;
1478 marks.push_back (mark);
1487 bbt_nmarks = bbt_bars + 2;
1488 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1489 if ((*i).is_bar()) {
1490 if ((*i).bar % 4 == 1) {
1491 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1492 mark.style = ArdourCanvas::Ruler::Mark::Major;
1495 if ((*i).bar % 4 == 3) {
1496 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1498 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1502 mark.position = (*i).frame;
1503 marks.push_back (mark);
1513 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1515 _samples_ruler_interval = (upper - lower) / 5;
1519 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1522 framepos_t const ilower = (framepos_t) floor (lower);
1526 ArdourCanvas::Ruler::Mark mark;
1528 if (_session == 0) {
1533 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1534 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1536 mark.position = pos;
1537 mark.style = ArdourCanvas::Ruler::Mark::Major;
1538 marks.push_back (mark);
1543 sample_to_clock_parts ( framepos_t sample,
1544 framepos_t sample_rate,
1558 hrs = left / (sample_rate * 60 * 60 * 1000);
1559 left -= hrs * sample_rate * 60 * 60 * 1000;
1560 mins = left / (sample_rate * 60 * 1000);
1561 left -= mins * sample_rate * 60 * 1000;
1562 secs = left / (sample_rate * 1000);
1563 left -= secs * sample_rate * 1000;
1564 millisecs = left / sample_rate;
1566 *millisecs_p = millisecs;
1575 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1577 framepos_t fr = _session->frame_rate() * 1000;
1580 if (_session == 0) {
1585 /* to prevent 'flashing' */
1586 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1592 framecnt_t const range = (upper - lower) * 1000;
1594 if (range <= (fr / 10)) { /* 0-0.1 second */
1595 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1596 minsec_ruler_scale = minsec_show_msecs;
1597 minsec_mark_modulo = 10;
1598 minsec_nmarks = 2 + (range / minsec_mark_interval);
1599 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1600 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1601 minsec_ruler_scale = minsec_show_msecs;
1602 minsec_mark_modulo = 100;
1603 minsec_nmarks = 2 + (range / minsec_mark_interval);
1604 } else if (range <= fr) { /* 0-1 second */
1605 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1606 minsec_ruler_scale = minsec_show_msecs;
1607 minsec_mark_modulo = 200;
1608 minsec_nmarks = 2 + (range / minsec_mark_interval);
1609 } else if (range <= 2 * fr) { /* 1-2 seconds */
1610 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1611 minsec_ruler_scale = minsec_show_msecs;
1612 minsec_mark_modulo = 500;
1613 minsec_nmarks = 2 + (range / minsec_mark_interval);
1614 } else if (range <= 8 * fr) { /* 2-5 seconds */
1615 minsec_mark_interval = fr / 5; /* show 2 seconds */
1616 minsec_ruler_scale = minsec_show_msecs;
1617 minsec_mark_modulo = 1000;
1618 minsec_nmarks = 2 + (range / minsec_mark_interval);
1619 } else if (range <= 16 * fr) { /* 8-16 seconds */
1620 minsec_mark_interval = fr; /* show 1 seconds */
1621 minsec_ruler_scale = minsec_show_seconds;
1622 minsec_mark_modulo = 2;
1623 minsec_nmarks = 2 + (range / minsec_mark_interval);
1624 } else if (range <= 30 * fr) { /* 10-30 seconds */
1625 minsec_mark_interval = fr; /* show 1 seconds */
1626 minsec_ruler_scale = minsec_show_seconds;
1627 minsec_mark_modulo = 5;
1628 minsec_nmarks = 2 + (range / minsec_mark_interval);
1629 } else if (range <= 60 * fr) { /* 30-60 seconds */
1630 minsec_mark_interval = fr; /* show 1 seconds */
1631 minsec_ruler_scale = minsec_show_seconds;
1632 minsec_mark_modulo = 5;
1633 minsec_nmarks = 2 + (range / minsec_mark_interval);
1634 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1635 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1636 minsec_ruler_scale = minsec_show_seconds;
1637 minsec_mark_modulo = 3;
1638 minsec_nmarks = 2 + (range / minsec_mark_interval);
1639 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1640 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1641 minsec_ruler_scale = minsec_show_seconds;
1642 minsec_mark_modulo = 30;
1643 minsec_nmarks = 2 + (range / minsec_mark_interval);
1644 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1645 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1646 minsec_ruler_scale = minsec_show_seconds;
1647 minsec_mark_modulo = 120;
1648 minsec_nmarks = 2 + (range / minsec_mark_interval);
1649 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1650 minsec_mark_interval = 60 * fr; /* show 1 minute */
1651 minsec_ruler_scale = minsec_show_minutes;
1652 minsec_mark_modulo = 5;
1653 minsec_nmarks = 2 + (range / minsec_mark_interval);
1654 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1655 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1656 minsec_ruler_scale = minsec_show_minutes;
1657 minsec_mark_modulo = 10;
1658 minsec_nmarks = 2 + (range / minsec_mark_interval);
1659 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1660 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1661 minsec_ruler_scale = minsec_show_minutes;
1662 minsec_mark_modulo = 30;
1663 minsec_nmarks = 2 + (range / minsec_mark_interval);
1664 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1665 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1666 minsec_ruler_scale = minsec_show_minutes;
1667 minsec_mark_modulo = 60;
1668 minsec_nmarks = 2 + (range / minsec_mark_interval);
1669 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1670 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1671 minsec_ruler_scale = minsec_show_hours;
1672 minsec_mark_modulo = 2;
1673 minsec_nmarks = 2 + (range / minsec_mark_interval);
1676 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1677 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1679 /* Normally we do not need to know anything about the width of the canvas
1680 to set the ruler scale, because the caller has already determined
1681 the width and set lower + upper arguments to this function to match that.
1683 But in this case, where the range defined by lower and uppper can vary
1684 substantially (anything from 24hrs+ to several billion years)
1685 trying to decide which tick marks to show does require us to know
1686 about the available width.
1689 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1690 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1691 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1692 minsec_ruler_scale = minsec_show_many_hours;
1697 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1701 long hrs, mins, secs, millisecs;
1704 ArdourCanvas::Ruler::Mark mark;
1706 if (_session == 0) {
1710 /* to prevent 'flashing' */
1711 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1712 lower = lower - spacer;
1717 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1719 switch (minsec_ruler_scale) {
1721 case minsec_show_msecs:
1722 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1723 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1724 if (millisecs % minsec_mark_modulo == 0) {
1725 if (millisecs == 0) {
1726 mark.style = ArdourCanvas::Ruler::Mark::Major;
1728 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1730 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1733 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1736 mark.position = pos/1000.0;
1737 marks.push_back (mark);
1741 case minsec_show_seconds:
1742 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1743 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1744 if (secs % minsec_mark_modulo == 0) {
1746 mark.style = ArdourCanvas::Ruler::Mark::Major;
1748 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1750 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1753 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1756 mark.position = pos/1000.0;
1757 marks.push_back (mark);
1761 case minsec_show_minutes:
1762 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1763 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1764 if (mins % minsec_mark_modulo == 0) {
1766 mark.style = ArdourCanvas::Ruler::Mark::Major;
1768 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1770 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1773 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1776 mark.position = pos/1000.0;
1777 marks.push_back (mark);
1781 case minsec_show_hours:
1782 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1783 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1784 if (hrs % minsec_mark_modulo == 0) {
1785 mark.style = ArdourCanvas::Ruler::Mark::Major;
1786 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1789 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1792 mark.position = pos/1000.0;
1793 marks.push_back (mark);
1797 case minsec_show_many_hours:
1798 for (n = 0; n < minsec_nmarks; ) {
1799 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1800 if (hrs % minsec_mark_modulo == 0) {
1801 mark.style = ArdourCanvas::Ruler::Mark::Major;
1802 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1804 mark.position = pos/1000.0;
1805 marks.push_back (mark);
1808 pos += minsec_mark_interval;