2 Copyright (C) 2000 Paul Davis
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 #include "gtk2ardour-config.h"
24 #include <cstdio> // for sprintf, grrr
30 #include <gtk/gtkaction.h>
32 #include "canvas/container.h"
33 #include "canvas/canvas.h"
34 #include "canvas/ruler.h"
35 #include "canvas/debug.h"
36 #include "canvas/scroll_group.h"
38 #include "ardour/session.h"
39 #include "ardour/tempo.h"
40 #include "ardour/profile.h"
42 #include "gtkmm2ext/gtk_ui.h"
43 #include "gtkmm2ext/keyboard.h"
45 #include "ardour_ui.h"
49 #include "gui_thread.h"
50 #include "ruler_dialog.h"
51 #include "time_axis_view.h"
52 #include "editor_drag.h"
53 #include "editor_cursors.h"
54 #include "ui_config.h"
58 using namespace ARDOUR;
61 using namespace Editing;
63 /* the order here must match the "metric" enums in editor.h */
65 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
68 TimecodeMetric (Editor* e) : _editor (e) {}
70 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
71 _editor->metric_get_timecode (marks, lower, upper, maxchars);
78 class SamplesMetric : public ArdourCanvas::Ruler::Metric
81 SamplesMetric (Editor* e) : _editor (e) {}
83 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
84 _editor->metric_get_samples (marks, lower, upper, maxchars);
91 class BBTMetric : public ArdourCanvas::Ruler::Metric
94 BBTMetric (Editor* e) : _editor (e) {}
96 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
97 _editor->metric_get_bbt (marks, lower, upper, maxchars);
104 class MinsecMetric : public ArdourCanvas::Ruler::Metric
107 MinsecMetric (Editor* e) : _editor (e) {}
109 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
110 _editor->metric_get_minsec (marks, lower, upper, maxchars);
117 static ArdourCanvas::Ruler::Metric* _bbt_metric;
118 static ArdourCanvas::Ruler::Metric* _timecode_metric;
119 static ArdourCanvas::Ruler::Metric* _samples_metric;
120 static ArdourCanvas::Ruler::Metric* _minsec_metric;
123 Editor::initialize_rulers ()
125 ruler_grabbed_widget = 0;
127 Pango::FontDescription font (UIConfiguration::instance().get_SmallerFont());
129 _timecode_metric = new TimecodeMetric (this);
130 _bbt_metric = new BBTMetric (this);
131 _minsec_metric = new MinsecMetric (this);
132 _samples_metric = new SamplesMetric (this);
134 timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
135 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
136 timecode_ruler->set_font_description (font);
137 CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
140 samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
141 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
142 samples_ruler->set_font_description (font);
143 CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
145 minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
146 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
147 minsec_ruler->set_font_description (font);
148 CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
151 bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
152 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
153 bbt_ruler->set_font_description (font);
154 CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
157 using namespace Box_Helpers;
158 BoxList & lab_children = time_bars_vbox.children();
160 lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
161 lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
162 lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
163 lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
164 lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
165 lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
166 lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
167 lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
168 lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
169 lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
170 lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
172 /* 1 event handler to bind them all ... */
174 timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
175 minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
176 bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
177 samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
179 visible_timebars = 0; /*this will be changed below */
183 Editor::ruler_label_button_release (GdkEventButton* ev)
185 if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
187 ruler_dialog = new RulerDialog ();
189 ruler_dialog->present ();
196 Editor::popup_ruler_menu (framepos_t where, ItemType t)
198 using namespace Menu_Helpers;
200 if (editor_ruler_menu == 0) {
201 editor_ruler_menu = new Menu;
202 editor_ruler_menu->set_name ("ArdourContextMenu");
205 // always build from scratch
206 MenuList& ruler_items = editor_ruler_menu->items();
207 editor_ruler_menu->set_name ("ArdourContextMenu");
212 ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false)));
213 ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
214 ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
217 case RangeMarkerBarItem:
218 ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
219 ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
220 ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
223 case TransportMarkerBarItem:
224 ruler_items.push_back (MenuElem (_("New Loop range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_loop), where)));
225 ruler_items.push_back (MenuElem (_("New Punch range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_punch), where)));
228 case CdMarkerBarItem:
230 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true)));
234 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
238 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
242 /* proper headings would be nice
243 * but AFAICT the only way to get them will be to define a
244 * special GTK style for insensitive Elements or subclass MenuItem
246 //ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
247 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
248 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
249 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
250 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
251 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
252 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
253 if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
255 ruler_items.push_back (SeparatorElem ());
257 //ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
258 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
259 ruler_items.push_back (CheckMenuElem (_("Lock")));
261 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
262 vtl_lock->set_active(is_video_timeline_locked());
263 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
266 ruler_items.push_back (SeparatorElem ());
268 //ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
269 //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
270 ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
272 Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
273 if (!ARDOUR_UI::instance()->video_timeline->found_xjadeo()) {
274 xjadeo_toggle->set_sensitive(false);
276 xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
277 xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
285 if (!ruler_items.empty()) {
286 editor_ruler_menu->popup (1, gtk_get_current_event_time());
289 no_ruler_shown_update = false;
293 Editor::store_ruler_visibility ()
295 XMLNode* node = new XMLNode(X_("RulerVisibility"));
297 node->add_property (X_("timecode"), ruler_timecode_action->get_active() ? "yes": "no");
298 node->add_property (X_("bbt"), ruler_bbt_action->get_active() ? "yes": "no");
299 node->add_property (X_("samples"), ruler_samples_action->get_active() ? "yes": "no");
300 node->add_property (X_("minsec"), ruler_minsec_action->get_active() ? "yes": "no");
301 node->add_property (X_("tempo"), ruler_tempo_action->get_active() ? "yes": "no");
302 node->add_property (X_("meter"), ruler_meter_action->get_active() ? "yes": "no");
303 node->add_property (X_("marker"), ruler_marker_action->get_active() ? "yes": "no");
304 node->add_property (X_("rangemarker"), ruler_range_action->get_active() ? "yes": "no");
305 node->add_property (X_("transportmarker"), ruler_loop_punch_action->get_active() ? "yes": "no");
306 node->add_property (X_("cdmarker"), ruler_cd_marker_action->get_active() ? "yes": "no");
307 node->add_property (X_("videotl"), ruler_video_action->get_active() ? "yes": "no");
309 _session->add_extra_xml (*node);
310 _session->set_dirty ();
314 Editor::restore_ruler_visibility ()
316 XMLProperty const * prop;
317 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
319 no_ruler_shown_update = true;
322 if ((prop = node->property ("timecode")) != 0) {
323 if (string_is_affirmative (prop->value())) {
324 ruler_timecode_action->set_active (true);
326 ruler_timecode_action->set_active (false);
329 if ((prop = node->property ("bbt")) != 0) {
330 if (string_is_affirmative (prop->value())) {
331 ruler_bbt_action->set_active (true);
333 ruler_bbt_action->set_active (false);
336 if ((prop = node->property ("samples")) != 0) {
337 if (string_is_affirmative (prop->value())) {
338 ruler_samples_action->set_active (true);
340 ruler_samples_action->set_active (false);
343 if ((prop = node->property ("minsec")) != 0) {
344 if (string_is_affirmative (prop->value())) {
345 ruler_minsec_action->set_active (true);
347 ruler_minsec_action->set_active (false);
350 if ((prop = node->property ("tempo")) != 0) {
351 if (string_is_affirmative (prop->value())) {
352 ruler_tempo_action->set_active (true);
354 ruler_tempo_action->set_active (false);
357 if ((prop = node->property ("meter")) != 0) {
358 if (string_is_affirmative (prop->value())) {
359 ruler_meter_action->set_active (true);
361 ruler_meter_action->set_active (false);
364 if ((prop = node->property ("marker")) != 0) {
365 if (string_is_affirmative (prop->value())) {
366 ruler_marker_action->set_active (true);
368 ruler_marker_action->set_active (false);
371 if ((prop = node->property ("rangemarker")) != 0) {
372 if (string_is_affirmative (prop->value())) {
373 ruler_range_action->set_active (true);
375 ruler_range_action->set_active (false);
379 if ((prop = node->property ("transportmarker")) != 0) {
380 if (string_is_affirmative (prop->value())) {
381 ruler_loop_punch_action->set_active (true);
383 ruler_loop_punch_action->set_active (false);
387 if ((prop = node->property ("cdmarker")) != 0) {
388 if (string_is_affirmative (prop->value())) {
389 ruler_cd_marker_action->set_active (true);
391 ruler_cd_marker_action->set_active (false);
395 // this _session doesn't yet know about the cdmarker ruler
396 // as a benefit to the user who doesn't know the feature exists, show the ruler if
397 // any cd marks exist
398 ruler_cd_marker_action->set_active (false);
399 const Locations::LocationList & locs = _session->locations()->list();
400 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
401 if ((*i)->is_cd_marker()) {
402 ruler_cd_marker_action->set_active (true);
408 if ((prop = node->property ("videotl")) != 0) {
409 if (string_is_affirmative (prop->value())) {
410 ruler_video_action->set_active (true);
412 ruler_video_action->set_active (false);
418 no_ruler_shown_update = false;
419 update_ruler_visibility ();
423 Editor::update_ruler_visibility ()
425 int visible_timebars = 0;
427 if (no_ruler_shown_update) {
431 /* the order of the timebars is fixed, so we have to go through each one
432 * and adjust its position depending on what is shown.
434 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
435 * loop/punch, cd markers, location markers
443 /* gtk update probs require this (damn) */
446 range_mark_label.hide();
447 transport_mark_label.hide();
448 cd_mark_label.hide();
450 videotl_label.hide();
453 if (ruler_minsec_action->get_active()) {
454 old_unit_pos = minsec_ruler->position().y;
455 if (tbpos != old_unit_pos) {
456 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
458 minsec_ruler->show();
460 tbpos += timebar_height;
461 tbgpos += timebar_height;
464 minsec_ruler->hide();
468 if (ruler_timecode_action->get_active()) {
469 old_unit_pos = timecode_ruler->position().y;
470 if (tbpos != old_unit_pos) {
471 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
473 timecode_ruler->show();
474 timecode_label.show();
475 tbpos += timebar_height;
476 tbgpos += timebar_height;
479 timecode_ruler->hide();
480 timecode_label.hide();
483 if (ruler_samples_action->get_active()) {
484 old_unit_pos = samples_ruler->position().y;
485 if (tbpos != old_unit_pos) {
486 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
488 samples_ruler->show();
489 samples_label.show();
490 tbpos += timebar_height;
491 tbgpos += timebar_height;
494 samples_ruler->hide();
495 samples_label.hide();
498 if (ruler_bbt_action->get_active()) {
499 old_unit_pos = bbt_ruler->position().y;
500 if (tbpos != old_unit_pos) {
501 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
505 tbpos += timebar_height;
506 tbgpos += timebar_height;
513 if (ruler_meter_action->get_active()) {
514 old_unit_pos = meter_group->position().y;
515 if (tbpos != old_unit_pos) {
516 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
520 tbpos += timebar_height;
521 tbgpos += timebar_height;
528 if (ruler_tempo_action->get_active()) {
529 old_unit_pos = tempo_group->position().y;
530 if (tbpos != old_unit_pos) {
531 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
535 tbpos += timebar_height;
536 tbgpos += timebar_height;
543 if (ruler_range_action->get_active()) {
544 old_unit_pos = range_marker_group->position().y;
545 if (tbpos != old_unit_pos) {
546 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
548 range_marker_group->show();
549 range_mark_label.show();
551 tbpos += timebar_height;
552 tbgpos += timebar_height;
555 range_marker_group->hide();
556 range_mark_label.hide();
559 if (ruler_loop_punch_action->get_active()) {
560 old_unit_pos = transport_marker_group->position().y;
561 if (tbpos != old_unit_pos) {
562 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
564 transport_marker_group->show();
565 transport_mark_label.show();
566 tbpos += timebar_height;
567 tbgpos += timebar_height;
570 transport_marker_group->hide();
571 transport_mark_label.hide();
574 if (ruler_cd_marker_action->get_active()) {
575 old_unit_pos = cd_marker_group->position().y;
576 if (tbpos != old_unit_pos) {
577 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
579 cd_marker_group->show();
580 cd_mark_label.show();
581 tbpos += timebar_height;
582 tbgpos += timebar_height;
584 // make sure all cd markers show up in their respective places
585 update_cd_marker_display();
587 cd_marker_group->hide();
588 cd_mark_label.hide();
589 // make sure all cd markers show up in their respective places
590 update_cd_marker_display();
593 if (ruler_marker_action->get_active()) {
594 old_unit_pos = marker_group->position().y;
595 if (tbpos != old_unit_pos) {
596 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
598 marker_group->show();
600 tbpos += timebar_height;
601 tbgpos += timebar_height;
604 marker_group->hide();
608 if (ruler_video_action->get_active()) {
609 old_unit_pos = videotl_group->position().y;
610 if (tbpos != old_unit_pos) {
611 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
613 videotl_group->show();
614 videotl_label.show();
615 tbpos += timebar_height * videotl_bar_height;
616 tbgpos += timebar_height * videotl_bar_height;
617 visible_timebars+=videotl_bar_height;
618 queue_visual_videotimeline_update();
620 videotl_group->hide();
621 videotl_label.hide();
622 update_video_timeline(true);
625 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
627 /* move hv_scroll_group (trackviews) to the end of the timebars
630 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
632 compute_fixed_ruler_scale ();
633 update_fixed_rulers();
634 redisplay_tempo (false);
636 /* Changing ruler visibility means that any lines on markers might need updating */
637 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
638 i->second->setup_lines ();
643 Editor::update_just_timecode ()
645 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
651 framepos_t rightmost_frame = leftmost_frame + current_page_samples();
653 if (ruler_timecode_action->get_active()) {
654 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
659 Editor::compute_fixed_ruler_scale ()
665 if (ruler_timecode_action->get_active()) {
666 set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
669 if (ruler_minsec_action->get_active()) {
670 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
673 if (ruler_samples_action->get_active()) {
674 set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
679 Editor::update_fixed_rulers ()
681 framepos_t rightmost_frame;
687 compute_fixed_ruler_scale ();
689 _timecode_metric->units_per_pixel = samples_per_pixel;
690 _samples_metric->units_per_pixel = samples_per_pixel;
691 _minsec_metric->units_per_pixel = samples_per_pixel;
693 rightmost_frame = leftmost_frame + current_page_samples();
695 /* these force a redraw, which in turn will force execution of the metric callbacks
696 to compute the relevant ticks to display.
699 if (ruler_timecode_action->get_active()) {
700 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
703 if (ruler_samples_action->get_active()) {
704 samples_ruler->set_range (leftmost_frame, rightmost_frame);
707 if (ruler_minsec_action->get_active()) {
708 minsec_ruler->set_range (leftmost_frame, rightmost_frame);
713 Editor::update_tempo_based_rulers (std::vector<TempoMap::BBTPoint>& grid)
719 compute_bbt_ruler_scale (grid, leftmost_frame, leftmost_frame+current_page_samples());
721 _bbt_metric->units_per_pixel = samples_per_pixel;
723 if (ruler_bbt_action->get_active()) {
724 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
730 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
741 fr = _session->frame_rate();
743 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
744 lower = lower - spacer;
749 upper = upper + spacer;
750 framecnt_t const range = upper - lower;
752 if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
753 timecode_ruler_scale = timecode_show_bits;
754 timecode_mark_modulo = 20;
755 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
756 } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
757 timecode_ruler_scale = timecode_show_frames;
758 timecode_mark_modulo = 1;
759 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
760 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
761 timecode_ruler_scale = timecode_show_frames;
762 timecode_mark_modulo = 2;
763 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
764 } else if (range <= fr) { /* 0.5-1 second */
765 timecode_ruler_scale = timecode_show_frames;
766 timecode_mark_modulo = 5;
767 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
768 } else if (range <= 2 * fr) { /* 1-2 seconds */
769 timecode_ruler_scale = timecode_show_frames;
770 timecode_mark_modulo = 10;
771 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
772 } else if (range <= 8 * fr) { /* 2-8 seconds */
773 timecode_ruler_scale = timecode_show_seconds;
774 timecode_mark_modulo = 1;
775 timecode_nmarks = 2 + (range / fr);
776 } else if (range <= 16 * fr) { /* 8-16 seconds */
777 timecode_ruler_scale = timecode_show_seconds;
778 timecode_mark_modulo = 2;
779 timecode_nmarks = 2 + (range / fr);
780 } else if (range <= 30 * fr) { /* 16-30 seconds */
781 timecode_ruler_scale = timecode_show_seconds;
782 timecode_mark_modulo = 5;
783 timecode_nmarks = 2 + (range / fr);
784 } else if (range <= 60 * fr) { /* 30-60 seconds */
785 timecode_ruler_scale = timecode_show_seconds;
786 timecode_mark_modulo = 5;
787 timecode_nmarks = 2 + (range / fr);
788 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
789 timecode_ruler_scale = timecode_show_seconds;
790 timecode_mark_modulo = 15;
791 timecode_nmarks = 2 + (range / fr);
792 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
793 timecode_ruler_scale = timecode_show_seconds;
794 timecode_mark_modulo = 30;
795 timecode_nmarks = 2 + (range / fr);
796 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
797 timecode_ruler_scale = timecode_show_minutes;
798 timecode_mark_modulo = 2;
799 timecode_nmarks = 2 + 10;
800 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
801 timecode_ruler_scale = timecode_show_minutes;
802 timecode_mark_modulo = 5;
803 timecode_nmarks = 2 + 30;
804 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
805 timecode_ruler_scale = timecode_show_minutes;
806 timecode_mark_modulo = 10;
807 timecode_nmarks = 2 + 60;
808 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
809 timecode_ruler_scale = timecode_show_minutes;
810 timecode_mark_modulo = 30;
811 timecode_nmarks = 2 + (60 * 4);
812 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
813 timecode_ruler_scale = timecode_show_hours;
814 timecode_mark_modulo = 1;
815 timecode_nmarks = 2 + 8;
816 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
817 timecode_ruler_scale = timecode_show_hours;
818 timecode_mark_modulo = 1;
819 timecode_nmarks = 2 + 24;
822 const framecnt_t hours_in_range = range / (60 * 60 * fr);
823 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
825 /* Normally we do not need to know anything about the width of the canvas
826 to set the ruler scale, because the caller has already determined
827 the width and set lower + upper arguments to this function to match that.
829 But in this case, where the range defined by lower and uppper can vary
830 substantially (basically anything from 24hrs+ to several billion years)
831 trying to decide which tick marks to show does require us to know
832 about the available width.
835 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
836 timecode_ruler_scale = timecode_show_many_hours;
837 timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
842 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
846 Timecode::Time timecode;
849 ArdourCanvas::Ruler::Mark mark;
855 if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
856 lower = lower - spacer;
861 pos = (framecnt_t) floor (lower);
863 switch (timecode_ruler_scale) {
864 case timecode_show_bits:
865 // Find timecode time of this sample (pos) with subframe accuracy
866 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
867 for (n = 0; n < timecode_nmarks; n++) {
868 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
869 if ((timecode.subframes % timecode_mark_modulo) == 0) {
870 if (timecode.subframes == 0) {
871 mark.style = ArdourCanvas::Ruler::Mark::Major;
872 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
874 mark.style = ArdourCanvas::Ruler::Mark::Minor;
875 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
878 snprintf (buf, sizeof(buf)," ");
879 mark.style = ArdourCanvas::Ruler::Mark::Micro;
883 marks.push_back (mark);
884 // Increment subframes by one
885 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
889 case timecode_show_frames:
890 // Find timecode time of this sample (pos)
891 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
892 // Go to next whole frame down
893 Timecode::frames_floor( timecode );
894 for (n = 0; n < timecode_nmarks; n++) {
895 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
896 if ((timecode.frames % timecode_mark_modulo) == 0) {
897 if (timecode.frames == 0) {
898 mark.style = ArdourCanvas::Ruler::Mark::Major;
900 mark.style = ArdourCanvas::Ruler::Mark::Minor;
903 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
905 snprintf (buf, sizeof(buf)," ");
906 mark.style = ArdourCanvas::Ruler::Mark::Micro;
910 marks.push_back (mark);
911 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
915 case timecode_show_seconds:
916 // Find timecode time of this sample (pos)
917 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
918 // Go to next whole second down
919 Timecode::seconds_floor( timecode );
920 for (n = 0; n < timecode_nmarks; n++) {
921 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
922 if ((timecode.seconds % timecode_mark_modulo) == 0) {
923 if (timecode.seconds == 0) {
924 mark.style = ArdourCanvas::Ruler::Mark::Major;
927 mark.style = ArdourCanvas::Ruler::Mark::Minor;
930 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
932 snprintf (buf, sizeof(buf)," ");
933 mark.style = ArdourCanvas::Ruler::Mark::Micro;
937 marks.push_back (mark);
938 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
942 case timecode_show_minutes:
943 //Find timecode time of this sample (pos)
944 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
945 // Go to next whole minute down
946 Timecode::minutes_floor( timecode );
947 for (n = 0; n < timecode_nmarks; n++) {
948 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
949 if ((timecode.minutes % timecode_mark_modulo) == 0) {
950 if (timecode.minutes == 0) {
951 mark.style = ArdourCanvas::Ruler::Mark::Major;
953 mark.style = ArdourCanvas::Ruler::Mark::Minor;
955 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
957 snprintf (buf, sizeof(buf)," ");
958 mark.style = ArdourCanvas::Ruler::Mark::Micro;
962 marks.push_back (mark);
963 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
966 case timecode_show_hours:
967 // Find timecode time of this sample (pos)
968 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
969 // Go to next whole hour down
970 Timecode::hours_floor( timecode );
971 for (n = 0; n < timecode_nmarks; n++) {
972 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
973 if ((timecode.hours % timecode_mark_modulo) == 0) {
974 mark.style = ArdourCanvas::Ruler::Mark::Major;
975 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
977 snprintf (buf, sizeof(buf)," ");
978 mark.style = ArdourCanvas::Ruler::Mark::Micro;
982 marks.push_back (mark);
983 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
986 case timecode_show_many_hours:
987 // Find timecode time of this sample (pos)
988 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
989 // Go to next whole hour down
990 Timecode::hours_floor (timecode);
992 for (n = 0; n < timecode_nmarks; ) {
993 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
994 if ((timecode.hours % timecode_mark_modulo) == 0) {
995 mark.style = ArdourCanvas::Ruler::Mark::Major;
996 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
999 marks.push_back (mark);
1002 /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
1003 and doing it 1 hour at a time is just stupid (and slow).
1005 timecode.hours += timecode_mark_modulo;
1012 Editor::compute_bbt_ruler_scale (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, framepos_t lower, framepos_t upper)
1014 if (_session == 0) {
1018 std::vector<TempoMap::BBTPoint>::const_iterator i;
1019 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1020 framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor(_session->tempo_map().beat_at_frame (lower)));
1021 framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (_session->tempo_map().beat_at_frame (upper)) + 1.0);
1023 _session->bbt_time (beat_before_lower_pos, lower_beat);
1024 _session->bbt_time (beat_after_upper_pos, upper_beat);
1027 bbt_accent_modulo = 1;
1028 bbt_bar_helper_on = false;
1032 bbt_ruler_scale = bbt_show_many;
1034 switch (_snap_type) {
1035 case SnapToBeatDiv2:
1036 bbt_beat_subdivision = 2;
1038 case SnapToBeatDiv3:
1039 bbt_beat_subdivision = 3;
1041 case SnapToBeatDiv4:
1042 bbt_beat_subdivision = 4;
1044 case SnapToBeatDiv5:
1045 bbt_beat_subdivision = 5;
1046 bbt_accent_modulo = 2; // XXX YIKES
1048 case SnapToBeatDiv6:
1049 bbt_beat_subdivision = 6;
1050 bbt_accent_modulo = 2; // XXX YIKES
1052 case SnapToBeatDiv7:
1053 bbt_beat_subdivision = 7;
1054 bbt_accent_modulo = 2; // XXX YIKES
1056 case SnapToBeatDiv8:
1057 bbt_beat_subdivision = 8;
1058 bbt_accent_modulo = 2;
1060 case SnapToBeatDiv10:
1061 bbt_beat_subdivision = 10;
1062 bbt_accent_modulo = 2; // XXX YIKES
1064 case SnapToBeatDiv12:
1065 bbt_beat_subdivision = 12;
1066 bbt_accent_modulo = 3;
1068 case SnapToBeatDiv14:
1069 bbt_beat_subdivision = 14;
1070 bbt_accent_modulo = 3; // XXX YIKES!
1072 case SnapToBeatDiv16:
1073 bbt_beat_subdivision = 16;
1074 bbt_accent_modulo = 4;
1076 case SnapToBeatDiv20:
1077 bbt_beat_subdivision = 20;
1078 bbt_accent_modulo = 5;
1080 case SnapToBeatDiv24:
1081 bbt_beat_subdivision = 24;
1082 bbt_accent_modulo = 6;
1084 case SnapToBeatDiv28:
1085 bbt_beat_subdivision = 28;
1086 bbt_accent_modulo = 7;
1088 case SnapToBeatDiv32:
1089 bbt_beat_subdivision = 32;
1090 bbt_accent_modulo = 8;
1092 case SnapToBeatDiv64:
1093 bbt_beat_subdivision = 64;
1094 bbt_accent_modulo = 8;
1096 case SnapToBeatDiv128:
1097 bbt_beat_subdivision = 128;
1098 bbt_accent_modulo = 8;
1101 bbt_beat_subdivision = 4;
1104 if (distance (grid.begin(), grid.end()) == 0) {
1113 if ((*i).beat >= (*grid.begin()).beat) {
1114 bbt_bars = (*i).bar - (*grid.begin()).bar;
1116 bbt_bars = (*i).bar - (*grid.begin()).bar;
1119 /*XXX totally wrong */
1120 bbt_bars = (floor (_session->tempo_map().beat_at_frame (upper)) - floor (_session->tempo_map().beat_at_frame (lower))) / 4;
1122 beats = distance (grid.begin(), grid.end()) - bbt_bars;
1124 /* Only show the bar helper if there aren't many bars on the screen */
1125 if ((bbt_bars < 2) || (beats < 5)) {
1126 bbt_bar_helper_on = true;
1129 if (bbt_bars > 8192) {
1130 bbt_ruler_scale = bbt_show_many;
1131 } else if (bbt_bars > 1024) {
1132 bbt_ruler_scale = bbt_show_64;
1133 } else if (bbt_bars > 256) {
1134 bbt_ruler_scale = bbt_show_16;
1135 } else if (bbt_bars > 64) {
1136 bbt_ruler_scale = bbt_show_4;
1137 } else if (bbt_bars > 10) {
1138 bbt_ruler_scale = bbt_show_1;
1139 } else if (bbt_bars > 2) {
1140 bbt_ruler_scale = bbt_show_beats;
1141 } else if (bbt_bars > 0) {
1142 bbt_ruler_scale = bbt_show_ticks;
1144 bbt_ruler_scale = bbt_show_ticks_detail;
1147 if ((bbt_ruler_scale == bbt_show_ticks_detail) && beats < 3) {
1148 bbt_ruler_scale = bbt_show_ticks_super_detail;
1153 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1155 ArdourCanvas::Ruler::Mark copy = marks.back();
1156 copy.label = newlabel;
1158 marks.push_back (copy);
1162 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1164 if (_session == 0) {
1168 std::vector<TempoMap::BBTPoint>::const_iterator i;
1173 Timecode::BBT_Time next_beat;
1178 double bbt_position_of_helper;
1179 bool i_am_accented = false;
1180 bool helper_active = false;
1181 ArdourCanvas::Ruler::Mark mark;
1183 std::vector<TempoMap::BBTPoint> grid;
1185 compute_current_bbt_points (grid, lower, upper);
1187 if (distance (grid.begin(), grid.end()) == 0) {
1191 switch (bbt_ruler_scale) {
1193 case bbt_show_beats:
1195 beats = distance (grid.begin(), grid.end());
1196 bbt_nmarks = beats + 2;
1199 mark.position = lower;
1200 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1201 marks.push_back (mark);
1203 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1205 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1206 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1207 edit_last_mark_label (marks, buf);
1208 helper_active = true;
1211 if ((*i).is_bar()) {
1212 mark.style = ArdourCanvas::Ruler::Mark::Major;
1213 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1214 } else if (((*i).beat % 2 == 1)) {
1215 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1218 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1222 mark.position = (*i).frame;
1223 marks.push_back (mark);
1229 case bbt_show_ticks:
1231 beats = distance (grid.begin(), grid.end());
1232 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1234 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1236 // could do marks.assign() here to preallocate
1239 mark.position = lower;
1240 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1241 marks.push_back (mark);
1243 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1245 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1246 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1247 edit_last_mark_label (marks, buf);
1248 helper_active = true;
1251 if ((*i).is_bar()) {
1252 mark.style = ArdourCanvas::Ruler::Mark::Major;
1253 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1255 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1256 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1258 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1262 mark.position = (*i).frame;
1263 marks.push_back (mark);
1267 /* Add the tick marks */
1268 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1269 tick = skip; // the first non-beat tick
1271 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1273 next_beat.beats = (*i).beat;
1274 next_beat.bars = (*i).bar;
1275 next_beat.ticks = tick;
1276 pos = _session->tempo_map().frame_time (next_beat);
1278 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1279 i_am_accented = true;
1282 mark.position = pos;
1284 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1285 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1287 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1289 i_am_accented = false;
1290 marks.push_back (mark);
1300 case bbt_show_ticks_detail:
1302 beats = distance (grid.begin(), grid.end());
1303 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1305 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1308 mark.position = lower;
1309 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1310 marks.push_back (mark);
1312 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1314 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1315 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1316 edit_last_mark_label (marks, buf);
1317 helper_active = true;
1320 if ((*i).is_bar()) {
1321 mark.style = ArdourCanvas::Ruler::Mark::Major;
1322 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1324 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1325 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1327 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1331 mark.position = (*i).frame;
1332 marks.push_back (mark);
1336 /* Add the tick marks */
1337 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1338 tick = skip; // the first non-beat tick
1341 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1343 next_beat.beats = (*i).beat;
1344 next_beat.bars = (*i).bar;
1345 next_beat.ticks = tick;
1346 pos = _session->tempo_map().frame_time (next_beat);
1348 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1349 i_am_accented = true;
1351 if (i_am_accented && (pos > bbt_position_of_helper)){
1352 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1358 mark.position = pos;
1360 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1361 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1363 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1365 i_am_accented = false;
1366 marks.push_back (mark);
1376 case bbt_show_ticks_super_detail:
1378 beats = distance (grid.begin(), grid.end());
1379 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1381 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1384 mark.position = lower;
1385 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1386 marks.push_back (mark);
1388 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1390 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1391 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1392 edit_last_mark_label (marks, buf);
1393 helper_active = true;
1396 if ((*i).is_bar()) {
1397 mark.style = ArdourCanvas::Ruler::Mark::Major;
1398 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1400 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1401 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1403 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1407 mark.position = (*i).frame;
1408 marks.push_back (mark);
1412 /* Add the tick marks */
1413 skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1415 next_beat.beats = (*i).beat;
1416 next_beat.bars = (*i).bar;
1417 tick = skip; // the first non-beat tick
1419 while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1421 next_beat.ticks = tick;
1422 pos = _session->tempo_map().frame_time (next_beat);
1423 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1424 i_am_accented = true;
1427 if (pos > bbt_position_of_helper) {
1428 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1434 mark.position = pos;
1436 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1437 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1439 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1441 i_am_accented = false;
1442 marks.push_back (mark);
1454 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1455 mark.style = ArdourCanvas::Ruler::Mark::Major;
1457 mark.position = lower;
1458 marks.push_back (mark);
1462 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1463 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1464 if ((*i).is_bar()) {
1465 if ((*i).bar % 64 == 1) {
1466 if ((*i).bar % 256 == 1) {
1467 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1468 mark.style = ArdourCanvas::Ruler::Mark::Major;
1471 if ((*i).bar % 256 == 129) {
1472 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1474 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1478 mark.position = (*i).frame;
1479 marks.push_back (mark);
1487 bbt_nmarks = (bbt_bars / 16) + 1;
1488 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1489 if ((*i).is_bar()) {
1490 if ((*i).bar % 16 == 1) {
1491 if ((*i).bar % 64 == 1) {
1492 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1493 mark.style = ArdourCanvas::Ruler::Mark::Major;
1496 if ((*i).bar % 64 == 33) {
1497 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1499 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1503 mark.position = (*i).frame;
1504 marks.push_back (mark);
1512 bbt_nmarks = (bbt_bars / 4) + 1;
1513 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1514 if ((*i).is_bar()) {
1515 if ((*i).bar % 4 == 1) {
1516 if ((*i).bar % 16 == 1) {
1517 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1518 mark.style = ArdourCanvas::Ruler::Mark::Major;
1521 if ((*i).bar % 16 == 9) {
1522 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1524 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1528 mark.position = (*i).frame;
1529 marks.push_back (mark);
1538 bbt_nmarks = bbt_bars + 2;
1539 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1540 if ((*i).is_bar()) {
1541 if ((*i).bar % 4 == 1) {
1542 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1543 mark.style = ArdourCanvas::Ruler::Mark::Major;
1546 if ((*i).bar % 4 == 3) {
1547 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1549 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1553 mark.position = (*i).frame;
1554 marks.push_back (mark);
1564 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1566 _samples_ruler_interval = (upper - lower) / 5;
1570 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1573 framepos_t const ilower = (framepos_t) floor (lower);
1577 ArdourCanvas::Ruler::Mark mark;
1579 if (_session == 0) {
1584 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1585 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1587 mark.position = pos;
1588 mark.style = ArdourCanvas::Ruler::Mark::Major;
1589 marks.push_back (mark);
1594 sample_to_clock_parts ( framepos_t sample,
1595 framepos_t sample_rate,
1609 hrs = left / (sample_rate * 60 * 60 * 1000);
1610 left -= hrs * sample_rate * 60 * 60 * 1000;
1611 mins = left / (sample_rate * 60 * 1000);
1612 left -= mins * sample_rate * 60 * 1000;
1613 secs = left / (sample_rate * 1000);
1614 left -= secs * sample_rate * 1000;
1615 millisecs = left / sample_rate;
1617 *millisecs_p = millisecs;
1626 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1628 framepos_t fr = _session->frame_rate() * 1000;
1631 if (_session == 0) {
1636 /* to prevent 'flashing' */
1637 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1643 framecnt_t const range = (upper - lower) * 1000;
1645 if (range <= (fr / 10)) { /* 0-0.1 second */
1646 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1647 minsec_ruler_scale = minsec_show_msecs;
1648 minsec_mark_modulo = 10;
1649 minsec_nmarks = 2 + (range / minsec_mark_interval);
1650 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1651 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1652 minsec_ruler_scale = minsec_show_msecs;
1653 minsec_mark_modulo = 100;
1654 minsec_nmarks = 2 + (range / minsec_mark_interval);
1655 } else if (range <= fr) { /* 0-1 second */
1656 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1657 minsec_ruler_scale = minsec_show_msecs;
1658 minsec_mark_modulo = 200;
1659 minsec_nmarks = 2 + (range / minsec_mark_interval);
1660 } else if (range <= 2 * fr) { /* 1-2 seconds */
1661 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1662 minsec_ruler_scale = minsec_show_msecs;
1663 minsec_mark_modulo = 500;
1664 minsec_nmarks = 2 + (range / minsec_mark_interval);
1665 } else if (range <= 8 * fr) { /* 2-5 seconds */
1666 minsec_mark_interval = fr / 5; /* show 2 seconds */
1667 minsec_ruler_scale = minsec_show_msecs;
1668 minsec_mark_modulo = 1000;
1669 minsec_nmarks = 2 + (range / minsec_mark_interval);
1670 } else if (range <= 16 * fr) { /* 8-16 seconds */
1671 minsec_mark_interval = fr; /* show 1 seconds */
1672 minsec_ruler_scale = minsec_show_seconds;
1673 minsec_mark_modulo = 2;
1674 minsec_nmarks = 2 + (range / minsec_mark_interval);
1675 } else if (range <= 30 * fr) { /* 10-30 seconds */
1676 minsec_mark_interval = fr; /* show 1 seconds */
1677 minsec_ruler_scale = minsec_show_seconds;
1678 minsec_mark_modulo = 5;
1679 minsec_nmarks = 2 + (range / minsec_mark_interval);
1680 } else if (range <= 60 * fr) { /* 30-60 seconds */
1681 minsec_mark_interval = fr; /* show 1 seconds */
1682 minsec_ruler_scale = minsec_show_seconds;
1683 minsec_mark_modulo = 5;
1684 minsec_nmarks = 2 + (range / minsec_mark_interval);
1685 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1686 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1687 minsec_ruler_scale = minsec_show_seconds;
1688 minsec_mark_modulo = 3;
1689 minsec_nmarks = 2 + (range / minsec_mark_interval);
1690 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1691 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1692 minsec_ruler_scale = minsec_show_seconds;
1693 minsec_mark_modulo = 30;
1694 minsec_nmarks = 2 + (range / minsec_mark_interval);
1695 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1696 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1697 minsec_ruler_scale = minsec_show_seconds;
1698 minsec_mark_modulo = 120;
1699 minsec_nmarks = 2 + (range / minsec_mark_interval);
1700 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1701 minsec_mark_interval = 60 * fr; /* show 1 minute */
1702 minsec_ruler_scale = minsec_show_minutes;
1703 minsec_mark_modulo = 5;
1704 minsec_nmarks = 2 + (range / minsec_mark_interval);
1705 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1706 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1707 minsec_ruler_scale = minsec_show_minutes;
1708 minsec_mark_modulo = 10;
1709 minsec_nmarks = 2 + (range / minsec_mark_interval);
1710 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1711 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1712 minsec_ruler_scale = minsec_show_minutes;
1713 minsec_mark_modulo = 30;
1714 minsec_nmarks = 2 + (range / minsec_mark_interval);
1715 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1716 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1717 minsec_ruler_scale = minsec_show_minutes;
1718 minsec_mark_modulo = 60;
1719 minsec_nmarks = 2 + (range / minsec_mark_interval);
1720 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1721 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1722 minsec_ruler_scale = minsec_show_hours;
1723 minsec_mark_modulo = 2;
1724 minsec_nmarks = 2 + (range / minsec_mark_interval);
1727 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1728 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1730 /* Normally we do not need to know anything about the width of the canvas
1731 to set the ruler scale, because the caller has already determined
1732 the width and set lower + upper arguments to this function to match that.
1734 But in this case, where the range defined by lower and uppper can vary
1735 substantially (anything from 24hrs+ to several billion years)
1736 trying to decide which tick marks to show does require us to know
1737 about the available width.
1740 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1741 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1742 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1743 minsec_ruler_scale = minsec_show_many_hours;
1748 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1752 long hrs, mins, secs, millisecs;
1755 ArdourCanvas::Ruler::Mark mark;
1757 if (_session == 0) {
1761 /* to prevent 'flashing' */
1762 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1763 lower = lower - spacer;
1768 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1770 switch (minsec_ruler_scale) {
1772 case minsec_show_msecs:
1773 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1774 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1775 if (millisecs % minsec_mark_modulo == 0) {
1776 if (millisecs == 0) {
1777 mark.style = ArdourCanvas::Ruler::Mark::Major;
1779 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1781 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1784 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1787 mark.position = pos/1000.0;
1788 marks.push_back (mark);
1792 case minsec_show_seconds:
1793 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1794 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1795 if (secs % minsec_mark_modulo == 0) {
1797 mark.style = ArdourCanvas::Ruler::Mark::Major;
1799 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1801 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1804 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1807 mark.position = pos/1000.0;
1808 marks.push_back (mark);
1812 case minsec_show_minutes:
1813 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1814 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1815 if (mins % minsec_mark_modulo == 0) {
1817 mark.style = ArdourCanvas::Ruler::Mark::Major;
1819 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1821 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1824 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1827 mark.position = pos/1000.0;
1828 marks.push_back (mark);
1832 case minsec_show_hours:
1833 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1834 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1835 if (hrs % minsec_mark_modulo == 0) {
1836 mark.style = ArdourCanvas::Ruler::Mark::Major;
1837 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1840 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1843 mark.position = pos/1000.0;
1844 marks.push_back (mark);
1848 case minsec_show_many_hours:
1849 for (n = 0; n < minsec_nmarks; ) {
1850 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1851 if (hrs % minsec_mark_modulo == 0) {
1852 mark.style = ArdourCanvas::Ruler::Mark::Major;
1853 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1855 mark.position = pos/1000.0;
1856 marks.push_back (mark);
1859 pos += minsec_mark_interval;