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/group.h"
33 #include "canvas/canvas.h"
34 #include "canvas/ruler.h"
35 #include "canvas/debug.h"
37 #include "ardour/session.h"
38 #include "ardour/tempo.h"
39 #include "ardour/profile.h"
41 #include "gtkmm2ext/gtk_ui.h"
46 #include "gtk-custom-hruler.h"
47 #include "gui_thread.h"
48 #include "time_axis_view.h"
49 #include "editor_drag.h"
50 #include "editor_cursors.h"
55 using namespace ARDOUR;
58 using namespace Editing;
60 /* the order here must match the "metric" enums in editor.h */
62 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
65 TimecodeMetric (Editor* e) : _editor (e) {}
67 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
68 _editor->metric_get_timecode (marks, lower, upper, maxchars);
75 class SamplesMetric : public ArdourCanvas::Ruler::Metric
78 SamplesMetric (Editor* e) : _editor (e) {}
80 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
81 _editor->metric_get_samples (marks, lower, upper, maxchars);
88 class BBTMetric : public ArdourCanvas::Ruler::Metric
91 BBTMetric (Editor* e) : _editor (e) {}
93 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
94 _editor->metric_get_bbt (marks, lower, upper, maxchars);
101 class MinsecMetric : public ArdourCanvas::Ruler::Metric
104 MinsecMetric (Editor* e) : _editor (e) {}
106 void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
107 _editor->metric_get_minsec (marks, lower, upper, maxchars);
114 static ArdourCanvas::Ruler::Metric* _bbt_metric;
115 static ArdourCanvas::Ruler::Metric* _timecode_metric;
116 static ArdourCanvas::Ruler::Metric* _samples_metric;
117 static ArdourCanvas::Ruler::Metric* _minsec_metric;
120 Editor::initialize_rulers ()
122 ruler_grabbed_widget = 0;
123 Pango::FontDescription font = get_font_for_style ("editor_time_ruler");
125 _timecode_metric = new TimecodeMetric (this);
126 _bbt_metric = new BBTMetric (this);
127 _minsec_metric = new MinsecMetric (this);
128 _samples_metric = new SamplesMetric (this);
130 timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric);
131 timecode_ruler->set_size (ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
132 timecode_ruler->set_font_description (font);
133 CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
136 samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric);
137 samples_ruler->set_size (ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
138 samples_ruler->set_font_description (font);
139 CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
141 minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric);
142 minsec_ruler->set_size (ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
143 minsec_ruler->set_font_description (font);
144 CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
147 bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric);
148 bbt_ruler->set_size (ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
149 bbt_ruler->set_font_description (font);
150 CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
153 using namespace Box_Helpers;
154 BoxList & lab_children = time_bars_vbox.children();
156 lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
157 lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
158 lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
159 lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
160 lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
161 lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
162 lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
163 lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
164 lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
165 lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
166 lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
168 // timecode_ruler->Event.connect (...);
169 // samples_ruler->Event.connect (...);
170 // bbt_ruler->Event.connect (...);
171 // minsec_ruler->Event.connect (...);
173 visible_timebars = 0; /*this will be changed below */
177 Editor::ruler_scroll (GdkEventScroll* event)
180 int direction = event->direction;
181 bool handled = false;
185 temporal_zoom_step (false);
189 case GDK_SCROLL_DOWN:
190 temporal_zoom_step (true);
194 case GDK_SCROLL_LEFT:
195 xdelta = (current_page_samples() / 2);
196 if (leftmost_frame > xdelta) {
197 reset_x_origin (leftmost_frame - xdelta);
204 case GDK_SCROLL_RIGHT:
205 xdelta = (current_page_samples() / 2);
206 if (max_framepos - xdelta > leftmost_frame) {
207 reset_x_origin (leftmost_frame + xdelta);
209 reset_x_origin (max_framepos - current_page_samples());
224 Editor::ruler_button_press (GdkEventButton* /*ev*/)
232 Widget * grab_widget = 0;
234 if (bbt_ruler->is_realized() && ev->window == bbt_ruler->get_window()->gobj()) {
235 grab_widget = bbt_ruler;
236 } else if (samples_ruler->is_realized() && ev->window == samples_ruler->get_window()->gobj()) {
237 grab_widget = samples_ruler;
238 } else if (minsec_ruler->is_realized() && ev->window == minsec_ruler->get_window()->gobj()) {
239 grab_widget = minsec_ruler;
243 grab_widget->add_modal_grab ();
244 ruler_grabbed_widget = grab_widget;
247 if (ev->button == 1) {
248 // Since we will locate the playhead on button release, cancel any running
250 if (_session->is_auditioning()) {
251 _session->cancel_audition ();
254 /* playhead cursor drag: CursorDrag expects an event with
255 * canvas coordinates, so convert from window coordinates,
256 * since for now, rulers are still Gtk::Widgets.
259 GdkEventButton canvas_ev = *ev;
260 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (ev->x, ev->y));
261 canvas_ev.x = rint (d.x);
262 canvas_ev.y = rint (d.y);
264 _drags->set (new CursorDrag (this, *playhead_cursor, false), reinterpret_cast<GdkEvent *> (&canvas_ev));
265 _dragging_playhead = true;
273 Editor::ruler_button_release (GdkEventButton* ev)
279 if (_drags->active ()) {
280 GdkEventButton canvas_ev = *ev;
281 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (ev->x, ev->y));
282 canvas_ev.x = rint (d.x);
283 canvas_ev.x = rint (d.y);
284 _drags->end_grab (reinterpret_cast<GdkEvent*> (&canvas_ev));
285 _dragging_playhead = false;
288 if (ev->button == 3) {
290 stop_canvas_autoscroll();
292 framepos_t where = window_event_sample ((GdkEvent*) ev);
295 popup_ruler_menu (where);
298 if (ruler_grabbed_widget) {
299 ruler_grabbed_widget->remove_modal_grab();
300 ruler_grabbed_widget = 0;
307 Editor::ruler_label_button_release (GdkEventButton* ev)
309 if (ev->button == 3) {
310 Gtk::Menu* m = dynamic_cast<Gtk::Menu*> (ActionManager::get_widget (X_("/RulerMenuPopup")));
312 m->popup (1, ev->time);
321 Editor::ruler_mouse_motion (GdkEventMotion* ev)
327 if (_drags->active ()) {
328 GdkEventMotion canvas_ev = *ev;
329 ArdourCanvas::Duple d = _track_canvas->window_to_canvas (ArdourCanvas::Duple (ev->x, ev->y));
330 canvas_ev.x = rint (d.x);
331 canvas_ev.y = rint (d.y);
332 _drags->window_motion_handler (reinterpret_cast<GdkEvent*> (&canvas_ev), false);
340 Editor::popup_ruler_menu (framepos_t where, ItemType t)
342 using namespace Menu_Helpers;
344 if (editor_ruler_menu == 0) {
345 editor_ruler_menu = new Menu;
346 editor_ruler_menu->set_name ("ArdourContextMenu");
349 // always build from scratch
350 MenuList& ruler_items = editor_ruler_menu->items();
351 editor_ruler_menu->set_name ("ArdourContextMenu");
356 ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false, false)));
357 ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
358 ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
359 ruler_items.push_back (SeparatorElem ());
361 case RangeMarkerBarItem:
362 ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
363 ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
364 ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
365 ruler_items.push_back (SeparatorElem ());
368 case TransportMarkerBarItem:
372 case CdMarkerBarItem:
374 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true, false)));
379 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
380 ruler_items.push_back (SeparatorElem ());
384 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
385 ruler_items.push_back (SeparatorElem ());
389 ruler_items.push_back (MenuElem (_("Timeline height")));
390 static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
391 ruler_items.push_back (CheckMenuElem (_("Large"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
392 if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
393 ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
394 if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
395 ruler_items.push_back (CheckMenuElem (_("Small"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
396 if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
397 ruler_items.push_back (SeparatorElem ());
399 ruler_items.push_back (MenuElem (_("Align Video Track")));
400 static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
402 ruler_items.push_back (CheckMenuElem (_("Lock")));
404 Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
405 vtl_lock->set_active(is_video_timeline_locked());
406 vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
409 ruler_items.push_back (SeparatorElem ());
416 Glib::RefPtr<Action> action;
418 action = ActionManager::get_action ("Rulers", "toggle-minsec-ruler");
420 ruler_items.push_back (MenuElem (*action->create_menu_item()));
422 if (!Profile->get_sae()) {
423 action = ActionManager::get_action ("Rulers", "toggle-timecode-ruler");
425 ruler_items.push_back (MenuElem (*action->create_menu_item()));
428 action = ActionManager::get_action ("Rulers", "toggle-samples-ruler");
430 ruler_items.push_back (MenuElem (*action->create_menu_item()));
432 action = ActionManager::get_action ("Rulers", "toggle-bbt-ruler");
434 ruler_items.push_back (MenuElem (*action->create_menu_item()));
436 action = ActionManager::get_action ("Rulers", "toggle-meter-ruler");
438 ruler_items.push_back (MenuElem (*action->create_menu_item()));
440 action = ActionManager::get_action ("Rulers", "toggle-tempo-ruler");
442 ruler_items.push_back (MenuElem (*action->create_menu_item()));
444 if (!Profile->get_sae()) {
445 action = ActionManager::get_action ("Rulers", "toggle-range-ruler");
447 ruler_items.push_back (MenuElem (*action->create_menu_item()));
450 action = ActionManager::get_action ("Rulers", "toggle-loop-punch-ruler");
452 ruler_items.push_back (MenuElem (*action->create_menu_item()));
454 action = ActionManager::get_action ("Rulers", "toggle-cd-marker-ruler");
456 ruler_items.push_back (MenuElem (*action->create_menu_item()));
458 action = ActionManager::get_action ("Rulers", "toggle-marker-ruler");
460 ruler_items.push_back (MenuElem (*action->create_menu_item()));
462 action = ActionManager::get_action ("Rulers", "toggle-video-ruler");
464 ruler_items.push_back (MenuElem (*action->create_menu_item()));
467 editor_ruler_menu->popup (1, gtk_get_current_event_time());
469 no_ruler_shown_update = false;
473 Editor::store_ruler_visibility ()
475 XMLNode* node = new XMLNode(X_("RulerVisibility"));
477 node->add_property (X_("timecode"), ruler_timecode_action->get_active() ? "yes": "no");
478 node->add_property (X_("bbt"), ruler_bbt_action->get_active() ? "yes": "no");
479 node->add_property (X_("samples"), ruler_samples_action->get_active() ? "yes": "no");
480 node->add_property (X_("minsec"), ruler_minsec_action->get_active() ? "yes": "no");
481 node->add_property (X_("tempo"), ruler_tempo_action->get_active() ? "yes": "no");
482 node->add_property (X_("meter"), ruler_meter_action->get_active() ? "yes": "no");
483 node->add_property (X_("marker"), ruler_marker_action->get_active() ? "yes": "no");
484 node->add_property (X_("rangemarker"), ruler_range_action->get_active() ? "yes": "no");
485 node->add_property (X_("transportmarker"), ruler_loop_punch_action->get_active() ? "yes": "no");
486 node->add_property (X_("cdmarker"), ruler_cd_marker_action->get_active() ? "yes": "no");
487 node->add_property (X_("videotl"), ruler_video_action->get_active() ? "yes": "no");
489 _session->add_extra_xml (*node);
490 _session->set_dirty ();
494 Editor::restore_ruler_visibility ()
497 XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
499 no_ruler_shown_update = true;
502 if ((prop = node->property ("timecode")) != 0) {
503 if (string_is_affirmative (prop->value())) {
504 ruler_timecode_action->set_active (true);
506 ruler_timecode_action->set_active (false);
509 if ((prop = node->property ("bbt")) != 0) {
510 if (string_is_affirmative (prop->value())) {
511 ruler_bbt_action->set_active (true);
513 ruler_bbt_action->set_active (false);
516 if ((prop = node->property ("samples")) != 0) {
517 if (string_is_affirmative (prop->value())) {
518 ruler_samples_action->set_active (true);
520 ruler_samples_action->set_active (false);
523 if ((prop = node->property ("minsec")) != 0) {
524 if (string_is_affirmative (prop->value())) {
525 ruler_minsec_action->set_active (true);
527 ruler_minsec_action->set_active (false);
530 if ((prop = node->property ("tempo")) != 0) {
531 if (string_is_affirmative (prop->value())) {
532 ruler_tempo_action->set_active (true);
534 ruler_tempo_action->set_active (false);
537 if ((prop = node->property ("meter")) != 0) {
538 if (string_is_affirmative (prop->value())) {
539 ruler_meter_action->set_active (true);
541 ruler_meter_action->set_active (false);
544 if ((prop = node->property ("marker")) != 0) {
545 if (string_is_affirmative (prop->value())) {
546 ruler_marker_action->set_active (true);
548 ruler_marker_action->set_active (false);
551 if ((prop = node->property ("rangemarker")) != 0) {
552 if (string_is_affirmative (prop->value())) {
553 ruler_range_action->set_active (true);
555 ruler_range_action->set_active (false);
559 if ((prop = node->property ("transportmarker")) != 0) {
560 if (string_is_affirmative (prop->value())) {
561 ruler_loop_punch_action->set_active (true);
563 ruler_loop_punch_action->set_active (false);
567 if ((prop = node->property ("cdmarker")) != 0) {
568 if (string_is_affirmative (prop->value())) {
569 ruler_cd_marker_action->set_active (true);
571 ruler_cd_marker_action->set_active (false);
575 // this _session doesn't yet know about the cdmarker ruler
576 // as a benefit to the user who doesn't know the feature exists, show the ruler if
577 // any cd marks exist
578 ruler_cd_marker_action->set_active (false);
579 const Locations::LocationList & locs = _session->locations()->list();
580 for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
581 if ((*i)->is_cd_marker()) {
582 ruler_cd_marker_action->set_active (true);
588 if ((prop = node->property ("videotl")) != 0) {
589 if (string_is_affirmative (prop->value())) {
590 ruler_video_action->set_active (true);
592 ruler_video_action->set_active (false);
598 no_ruler_shown_update = false;
599 update_ruler_visibility ();
603 Editor::update_ruler_visibility ()
605 int visible_timebars = 0;
607 if (no_ruler_shown_update) {
611 /* the order of the timebars is fixed, so we have to go through each one
612 * and adjust its position depending on what is shown.
614 * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
615 * loop/punch, cd markers, location markers
623 /* gtk update probs require this (damn) */
626 range_mark_label.hide();
627 transport_mark_label.hide();
628 cd_mark_label.hide();
630 videotl_label.hide();
633 if (ruler_minsec_action->get_active()) {
634 old_unit_pos = minsec_ruler->position().y;
635 if (tbpos != old_unit_pos) {
636 minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
638 minsec_ruler->show();
640 tbpos += timebar_height;
641 tbgpos += timebar_height;
644 minsec_ruler->hide();
648 if (ruler_timecode_action->get_active()) {
649 old_unit_pos = timecode_ruler->position().y;
650 if (tbpos != old_unit_pos) {
651 timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
653 timecode_ruler->show();
654 timecode_label.show();
655 tbpos += timebar_height;
656 tbgpos += timebar_height;
659 timecode_ruler->hide();
660 timecode_label.hide();
663 if (ruler_samples_action->get_active()) {
664 old_unit_pos = samples_ruler->position().y;
665 if (tbpos != old_unit_pos) {
666 samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
668 samples_ruler->show();
669 samples_label.show();
670 tbpos += timebar_height;
671 tbgpos += timebar_height;
674 samples_ruler->hide();
675 samples_label.hide();
678 if (ruler_bbt_action->get_active()) {
679 old_unit_pos = bbt_ruler->position().y;
680 if (tbpos != old_unit_pos) {
681 bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
685 tbpos += timebar_height;
686 tbgpos += timebar_height;
693 if (ruler_meter_action->get_active()) {
694 old_unit_pos = meter_group->position().y;
695 if (tbpos != old_unit_pos) {
696 meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
700 tbpos += timebar_height;
701 tbgpos += timebar_height;
708 if (ruler_tempo_action->get_active()) {
709 old_unit_pos = tempo_group->position().y;
710 if (tbpos != old_unit_pos) {
711 tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
715 tbpos += timebar_height;
716 tbgpos += timebar_height;
723 if (!Profile->get_sae() && ruler_range_action->get_active()) {
724 old_unit_pos = range_marker_group->position().y;
725 if (tbpos != old_unit_pos) {
726 range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
728 range_marker_group->show();
729 range_mark_label.show();
731 tbpos += timebar_height;
732 tbgpos += timebar_height;
735 range_marker_group->hide();
736 range_mark_label.hide();
739 if (ruler_loop_punch_action->get_active()) {
740 old_unit_pos = transport_marker_group->position().y;
741 if (tbpos != old_unit_pos) {
742 transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
744 transport_marker_group->show();
745 transport_mark_label.show();
746 tbpos += timebar_height;
747 tbgpos += timebar_height;
750 transport_marker_group->hide();
751 transport_mark_label.hide();
754 if (ruler_cd_marker_action->get_active()) {
755 old_unit_pos = cd_marker_group->position().y;
756 if (tbpos != old_unit_pos) {
757 cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
759 cd_marker_group->show();
760 cd_mark_label.show();
761 tbpos += timebar_height;
762 tbgpos += timebar_height;
764 // make sure all cd markers show up in their respective places
765 update_cd_marker_display();
767 cd_marker_group->hide();
768 cd_mark_label.hide();
769 // make sure all cd markers show up in their respective places
770 update_cd_marker_display();
773 if (ruler_marker_action->get_active()) {
774 old_unit_pos = marker_group->position().y;
775 if (tbpos != old_unit_pos) {
776 marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
778 marker_group->show();
780 tbpos += timebar_height;
781 tbgpos += timebar_height;
784 marker_group->hide();
788 if (ruler_video_action->get_active()) {
789 old_unit_pos = videotl_group->position().y;
790 if (tbpos != old_unit_pos) {
791 videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
793 videotl_group->show();
794 videotl_label.show();
795 tbpos += timebar_height * videotl_bar_height;
796 tbgpos += timebar_height * videotl_bar_height;
797 visible_timebars+=videotl_bar_height;
798 queue_visual_videotimeline_update();
800 videotl_group->hide();
801 videotl_label.hide();
802 update_video_timeline(true);
805 time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
807 /* move hv_scroll_group (trackviews) to the end of the timebars
810 hv_scroll_group->set_y_position (timebar_height * visible_timebars);
812 compute_fixed_ruler_scale ();
813 update_fixed_rulers();
814 redisplay_tempo (false);
816 /* Changing ruler visibility means that any lines on markers might need updating */
817 for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
818 i->second->setup_lines ();
823 Editor::update_just_timecode ()
825 ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
831 framepos_t rightmost_frame = leftmost_frame + current_page_samples();
833 if (ruler_timecode_action->get_active()) {
834 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
839 Editor::compute_fixed_ruler_scale ()
845 if (ruler_timecode_action->get_active()) {
846 set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
849 if (ruler_minsec_action->get_active()) {
850 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
853 if (ruler_samples_action->get_active()) {
854 set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
859 Editor::update_fixed_rulers ()
861 framepos_t rightmost_frame;
867 compute_fixed_ruler_scale ();
869 _timecode_metric->units_per_pixel = samples_per_pixel;
870 _samples_metric->units_per_pixel = samples_per_pixel;
871 _minsec_metric->units_per_pixel = samples_per_pixel;
873 rightmost_frame = leftmost_frame + current_page_samples();
875 /* these force a redraw, which in turn will force execution of the metric callbacks
876 to compute the relevant ticks to display.
879 if (ruler_timecode_action->get_active()) {
880 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
883 if (ruler_samples_action->get_active()) {
884 samples_ruler->set_range (leftmost_frame, rightmost_frame);
887 if (ruler_minsec_action->get_active()) {
888 minsec_ruler->set_range (leftmost_frame, rightmost_frame);
893 Editor::update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
894 ARDOUR::TempoMap::BBTPointList::const_iterator& end)
900 compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_samples(),
903 _bbt_metric->units_per_pixel = samples_per_pixel;
905 if (ruler_bbt_action->get_active()) {
906 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
911 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
920 fr = _session->frame_rate();
922 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
923 lower = lower - spacer;
927 upper = upper + spacer;
928 framecnt_t const range = upper - lower;
930 if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
931 timecode_ruler_scale = timecode_show_bits;
932 timecode_mark_modulo = 20;
933 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
934 } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
935 timecode_ruler_scale = timecode_show_frames;
936 timecode_mark_modulo = 1;
937 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
938 } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
939 timecode_ruler_scale = timecode_show_frames;
940 timecode_mark_modulo = 2;
941 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
942 } else if (range <= fr) { /* 0.5-1 second */
943 timecode_ruler_scale = timecode_show_frames;
944 timecode_mark_modulo = 5;
945 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
946 } else if (range <= 2 * fr) { /* 1-2 seconds */
947 timecode_ruler_scale = timecode_show_frames;
948 timecode_mark_modulo = 10;
949 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
950 } else if (range <= 8 * fr) { /* 2-8 seconds */
951 timecode_ruler_scale = timecode_show_seconds;
952 timecode_mark_modulo = 1;
953 timecode_nmarks = 2 + (range / fr);
954 } else if (range <= 16 * fr) { /* 8-16 seconds */
955 timecode_ruler_scale = timecode_show_seconds;
956 timecode_mark_modulo = 2;
957 timecode_nmarks = 2 + (range / fr);
958 } else if (range <= 30 * fr) { /* 16-30 seconds */
959 timecode_ruler_scale = timecode_show_seconds;
960 timecode_mark_modulo = 5;
961 timecode_nmarks = 2 + (range / fr);
962 } else if (range <= 60 * fr) { /* 30-60 seconds */
963 timecode_ruler_scale = timecode_show_seconds;
964 timecode_mark_modulo = 5;
965 timecode_nmarks = 2 + (range / fr);
966 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
967 timecode_ruler_scale = timecode_show_seconds;
968 timecode_mark_modulo = 15;
969 timecode_nmarks = 2 + (range / fr);
970 } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
971 timecode_ruler_scale = timecode_show_seconds;
972 timecode_mark_modulo = 30;
973 timecode_nmarks = 2 + (range / fr);
974 } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
975 timecode_ruler_scale = timecode_show_minutes;
976 timecode_mark_modulo = 2;
977 timecode_nmarks = 2 + 10;
978 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
979 timecode_ruler_scale = timecode_show_minutes;
980 timecode_mark_modulo = 5;
981 timecode_nmarks = 2 + 30;
982 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
983 timecode_ruler_scale = timecode_show_minutes;
984 timecode_mark_modulo = 10;
985 timecode_nmarks = 2 + 60;
986 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
987 timecode_ruler_scale = timecode_show_minutes;
988 timecode_mark_modulo = 30;
989 timecode_nmarks = 2 + (60 * 4);
990 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
991 timecode_ruler_scale = timecode_show_hours;
992 timecode_mark_modulo = 1;
993 timecode_nmarks = 2 + 8;
994 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
995 timecode_ruler_scale = timecode_show_hours;
996 timecode_mark_modulo = 1;
997 timecode_nmarks = 2 + 24;
1000 /* not possible if framepos_t is a 32 bit quantity */
1002 timecode_ruler_scale = timecode_show_hours;
1003 timecode_mark_modulo = 4;
1004 timecode_nmarks = 2 + 24;
1010 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1014 Timecode::Time timecode;
1017 ArdourCanvas::Ruler::Mark mark;
1019 if (_session == 0) {
1023 if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
1024 lower = lower - spacer;
1029 pos = (framecnt_t) floor (lower);
1031 switch (timecode_ruler_scale) {
1032 case timecode_show_bits:
1034 // Find timecode time of this sample (pos) with subframe accuracy
1035 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
1037 for (n = 0; n < timecode_nmarks; n++) {
1038 _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
1039 if ((timecode.subframes % timecode_mark_modulo) == 0) {
1040 if (timecode.subframes == 0) {
1041 mark.style = ArdourCanvas::Ruler::Mark::Major;
1042 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1044 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1045 snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
1048 snprintf (buf, sizeof(buf)," ");
1049 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1053 mark.position = pos;
1055 marks.push_back (mark);
1057 // Increment subframes by one
1058 Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
1061 case timecode_show_seconds:
1062 // Find timecode time of this sample (pos)
1063 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
1064 // Go to next whole second down
1065 Timecode::seconds_floor( timecode );
1067 for (n = 0; n < timecode_nmarks; n++) {
1068 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
1069 if ((timecode.seconds % timecode_mark_modulo) == 0) {
1070 if (timecode.seconds == 0) {
1071 mark.style = ArdourCanvas::Ruler::Mark::Major;
1072 mark.position = pos;
1074 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1075 mark.position = pos;
1077 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1079 snprintf (buf, sizeof(buf)," ");
1080 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1081 mark.position = pos;
1085 marks.push_back (mark);
1087 Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
1090 case timecode_show_minutes:
1091 // Find timecode time of this sample (pos)
1092 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
1093 // Go to next whole minute down
1094 Timecode::minutes_floor( timecode );
1096 for (n = 0; n < timecode_nmarks; n++) {
1097 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
1098 if ((timecode.minutes % timecode_mark_modulo) == 0) {
1099 if (timecode.minutes == 0) {
1100 mark.style = ArdourCanvas::Ruler::Mark::Major;
1102 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1104 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1106 snprintf (buf, sizeof(buf)," ");
1107 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1111 mark.position = pos;
1112 marks.push_back (mark);
1113 Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
1117 case timecode_show_hours:
1118 // Find timecode time of this sample (pos)
1119 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
1120 // Go to next whole hour down
1121 Timecode::hours_floor( timecode );
1123 for (n = 0; n < timecode_nmarks; n++) {
1124 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
1125 if ((timecode.hours % timecode_mark_modulo) == 0) {
1126 mark.style = ArdourCanvas::Ruler::Mark::Major;
1127 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1129 snprintf (buf, sizeof(buf)," ");
1130 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1134 mark.position = pos;
1135 marks.push_back (mark);
1136 Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
1139 case timecode_show_frames:
1140 // Find timecode time of this sample (pos)
1141 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
1142 // Go to next whole frame down
1143 Timecode::frames_floor( timecode );
1145 for (n = 0; n < timecode_nmarks; n++) {
1146 _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
1147 if ((timecode.frames % timecode_mark_modulo) == 0) {
1148 if (timecode.frames == 0) {
1149 mark.style = ArdourCanvas::Ruler::Mark::Major;
1151 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1153 mark.position = pos;
1154 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
1156 snprintf (buf, sizeof(buf)," ");
1157 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1158 mark.position = pos;
1162 marks.push_back (mark);
1163 Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
1173 Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
1174 ARDOUR::TempoMap::BBTPointList::const_iterator begin,
1175 ARDOUR::TempoMap::BBTPointList::const_iterator end)
1177 if (_session == 0) {
1181 TempoMap::BBTPointList::const_iterator i;
1182 Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1184 _session->bbt_time (lower, lower_beat);
1185 _session->bbt_time (upper, upper_beat);
1188 bbt_accent_modulo = 1;
1189 bbt_bar_helper_on = false;
1193 bbt_ruler_scale = bbt_over;
1195 switch (_snap_type) {
1196 case SnapToBeatDiv2:
1197 bbt_beat_subdivision = 2;
1199 case SnapToBeatDiv3:
1200 bbt_beat_subdivision = 3;
1202 case SnapToBeatDiv4:
1203 bbt_beat_subdivision = 4;
1205 case SnapToBeatDiv5:
1206 bbt_beat_subdivision = 5;
1207 bbt_accent_modulo = 2; // XXX YIKES
1209 case SnapToBeatDiv6:
1210 bbt_beat_subdivision = 6;
1211 bbt_accent_modulo = 2; // XXX YIKES
1213 case SnapToBeatDiv7:
1214 bbt_beat_subdivision = 7;
1215 bbt_accent_modulo = 2; // XXX YIKES
1217 case SnapToBeatDiv8:
1218 bbt_beat_subdivision = 8;
1219 bbt_accent_modulo = 2;
1221 case SnapToBeatDiv10:
1222 bbt_beat_subdivision = 10;
1223 bbt_accent_modulo = 2; // XXX YIKES
1225 case SnapToBeatDiv12:
1226 bbt_beat_subdivision = 12;
1227 bbt_accent_modulo = 3;
1229 case SnapToBeatDiv14:
1230 bbt_beat_subdivision = 14;
1231 bbt_accent_modulo = 3; // XXX YIKES!
1233 case SnapToBeatDiv16:
1234 bbt_beat_subdivision = 16;
1235 bbt_accent_modulo = 4;
1237 case SnapToBeatDiv20:
1238 bbt_beat_subdivision = 20;
1239 bbt_accent_modulo = 5;
1241 case SnapToBeatDiv24:
1242 bbt_beat_subdivision = 24;
1243 bbt_accent_modulo = 6;
1245 case SnapToBeatDiv28:
1246 bbt_beat_subdivision = 28;
1247 bbt_accent_modulo = 7;
1249 case SnapToBeatDiv32:
1250 bbt_beat_subdivision = 32;
1251 bbt_accent_modulo = 8;
1253 case SnapToBeatDiv64:
1254 bbt_beat_subdivision = 64;
1255 bbt_accent_modulo = 8;
1257 case SnapToBeatDiv128:
1258 bbt_beat_subdivision = 128;
1259 bbt_accent_modulo = 8;
1262 bbt_beat_subdivision = 4;
1266 if (distance (begin, end) == 0) {
1272 if ((*i).beat >= (*begin).beat) {
1273 bbt_bars = (*i).bar - (*begin).bar;
1275 bbt_bars = (*i).bar - (*begin).bar - 1;
1277 beats = distance (begin, end) - bbt_bars;
1279 /* Only show the bar helper if there aren't many bars on the screen */
1280 if ((bbt_bars < 2) || (beats < 5)) {
1281 bbt_bar_helper_on = true;
1284 if (bbt_bars > 8192) {
1285 bbt_ruler_scale = bbt_over;
1286 } else if (bbt_bars > 1024) {
1287 bbt_ruler_scale = bbt_show_64;
1288 } else if (bbt_bars > 256) {
1289 bbt_ruler_scale = bbt_show_16;
1290 } else if (bbt_bars > 64) {
1291 bbt_ruler_scale = bbt_show_4;
1292 } else if (bbt_bars > 10) {
1293 bbt_ruler_scale = bbt_show_1;
1294 } else if (bbt_bars > 2) {
1295 bbt_ruler_scale = bbt_show_beats;
1296 } else if (bbt_bars > 0) {
1297 bbt_ruler_scale = bbt_show_ticks;
1299 bbt_ruler_scale = bbt_show_ticks_detail;
1302 if ((bbt_ruler_scale == bbt_show_ticks_detail) && (lower_beat.beats == upper_beat.beats) && (upper_beat.ticks - lower_beat.ticks <= Timecode::BBT_Time::ticks_per_beat / 4)) {
1303 bbt_ruler_scale = bbt_show_ticks_super_detail;
1308 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1310 ArdourCanvas::Ruler::Mark copy = marks.back();
1311 copy.label = newlabel;
1313 marks.push_back (copy);
1317 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1319 if (_session == 0) {
1323 TempoMap::BBTPointList::const_iterator i;
1328 Timecode::BBT_Time next_beat;
1329 framepos_t next_beat_pos;
1334 framepos_t frame_skip;
1335 double frame_skip_error;
1336 double bbt_position_of_helper;
1337 double accumulated_error;
1338 bool i_am_accented = false;
1339 bool helper_active = false;
1340 ArdourCanvas::Ruler::Mark mark;
1342 ARDOUR::TempoMap::BBTPointList::const_iterator begin;
1343 ARDOUR::TempoMap::BBTPointList::const_iterator end;
1345 compute_current_bbt_points (lower, upper, begin, end);
1347 if (distance (begin, end) == 0) {
1351 switch (bbt_ruler_scale) {
1353 case bbt_show_beats:
1354 beats = distance (begin, end);
1355 bbt_nmarks = beats + 2;
1358 mark.position = lower;
1359 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1360 marks.push_back (mark);
1362 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1364 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1365 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1366 edit_last_mark_label (marks, buf);
1367 helper_active = true;
1370 if ((*i).is_bar()) {
1371 mark.style = ArdourCanvas::Ruler::Mark::Major;
1372 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1373 } else if (((*i).beat % 2 == 1)) {
1374 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1377 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1381 mark.position = (*i).frame;
1382 marks.push_back (mark);
1388 case bbt_show_ticks:
1390 beats = distance (begin, end);
1391 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1393 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1395 // could do marks.assign() here to preallocate
1398 mark.position = lower;
1399 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1400 marks.push_back (mark);
1402 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1404 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1405 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1406 edit_last_mark_label (marks, buf);
1407 helper_active = true;
1410 if ((*i).is_bar()) {
1411 mark.style = ArdourCanvas::Ruler::Mark::Major;
1412 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1414 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1415 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1417 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1421 mark.position = (*i).frame;
1422 marks.push_back (mark);
1426 /* Add the tick marks */
1428 /* Find the next beat */
1429 next_beat.beats = (*i).beat;
1430 next_beat.bars = (*i).bar;
1431 next_beat.ticks = 0;
1433 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1434 next_beat.beats += 1;
1436 next_beat.bars += 1;
1437 next_beat.beats = 1;
1440 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1442 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1443 frame_skip_error -= frame_skip;
1444 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1446 pos = (*i).frame + frame_skip;
1447 accumulated_error = frame_skip_error;
1451 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1453 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1454 i_am_accented = true;
1459 /* Error compensation for float to framepos_t*/
1460 accumulated_error += frame_skip_error;
1461 if (accumulated_error > 1) {
1463 accumulated_error -= 1.0f;
1466 mark.position = pos;
1468 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1469 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1471 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1473 i_am_accented = false;
1474 marks.push_back (mark);
1481 case bbt_show_ticks_detail:
1483 beats = distance (begin, end);
1484 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1486 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1489 mark.position = lower;
1490 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1491 marks.push_back (mark);
1493 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1495 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1496 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1497 edit_last_mark_label (marks, buf);
1498 helper_active = true;
1501 if ((*i).is_bar()) {
1502 mark.style = ArdourCanvas::Ruler::Mark::Major;
1503 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1505 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1506 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1508 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1512 mark.position = (*i).frame;
1513 marks.push_back (mark);
1517 /* Add the tick marks */
1519 /* Find the next beat */
1521 next_beat.beats = (*i).beat;
1522 next_beat.bars = (*i).bar;
1524 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1525 next_beat.beats += 1;
1527 next_beat.bars += 1;
1528 next_beat.beats = 1;
1531 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1533 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1534 frame_skip_error -= frame_skip;
1535 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1537 pos = (*i).frame + frame_skip;
1538 accumulated_error = frame_skip_error;
1542 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1544 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1545 i_am_accented = true;
1548 if (i_am_accented && (pos > bbt_position_of_helper)){
1549 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1556 /* Error compensation for float to framepos_t*/
1557 accumulated_error += frame_skip_error;
1558 if (accumulated_error > 1) {
1560 accumulated_error -= 1.0f;
1563 mark.position = pos;
1565 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1566 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1568 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1570 i_am_accented = false;
1577 case bbt_show_ticks_super_detail:
1579 beats = distance (begin, end);
1580 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1582 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1585 mark.position = lower;
1586 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1587 marks.push_back (mark);
1589 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1591 if ((*i).frame < lower && (bbt_bar_helper_on)) {
1592 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1593 edit_last_mark_label (marks, buf);
1594 helper_active = true;
1597 if ((*i).is_bar()) {
1598 mark.style = ArdourCanvas::Ruler::Mark::Major;
1599 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1601 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1602 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1604 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1608 mark.position = (*i).frame;
1609 marks.push_back (mark);
1613 /* Add the tick marks */
1615 /* Find the next beat */
1617 next_beat.beats = (*i).beat;
1618 next_beat.bars = (*i).bar;
1620 if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1621 next_beat.beats += 1;
1623 next_beat.bars += 1;
1624 next_beat.beats = 1;
1627 next_beat_pos = _session->tempo_map().frame_time(next_beat);
1629 frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() * 60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1630 frame_skip_error -= frame_skip;
1631 skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1633 pos = (*i).frame + frame_skip;
1634 accumulated_error = frame_skip_error;
1638 for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1640 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1641 i_am_accented = true;
1644 if (pos > bbt_position_of_helper) {
1645 snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1652 /* Error compensation for float to framepos_t*/
1653 accumulated_error += frame_skip_error;
1654 if (accumulated_error > 1) {
1656 accumulated_error -= 1.0f;
1659 mark.position = pos;
1661 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1662 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1664 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1666 i_am_accented = false;
1667 marks.push_back (mark);
1676 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1677 mark.style = ArdourCanvas::Ruler::Mark::Major;
1679 mark.position = lower;
1680 marks.push_back (mark);
1685 bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1686 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1687 if ((*i).is_bar()) {
1688 if ((*i).bar % 64 == 1) {
1689 if ((*i).bar % 256 == 1) {
1690 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1691 mark.style = ArdourCanvas::Ruler::Mark::Major;
1694 if ((*i).bar % 256 == 129) {
1695 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1697 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1701 mark.position = (*i).frame;
1702 marks.push_back (mark);
1710 bbt_nmarks = (bbt_bars / 16) + 1;
1711 for (n = 0, i = begin; i != end && n < bbt_nmarks; i++) {
1712 if ((*i).is_bar()) {
1713 if ((*i).bar % 16 == 1) {
1714 if ((*i).bar % 64 == 1) {
1715 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1716 mark.style = ArdourCanvas::Ruler::Mark::Major;
1719 if ((*i).bar % 64 == 33) {
1720 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1722 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1726 mark.position = (*i).frame;
1727 marks.push_back (mark);
1735 bbt_nmarks = (bbt_bars / 4) + 1;
1736 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1737 if ((*i).is_bar()) {
1738 if ((*i).bar % 4 == 1) {
1739 if ((*i).bar % 16 == 1) {
1740 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1741 mark.style = ArdourCanvas::Ruler::Mark::Major;
1744 if ((*i).bar % 16 == 9) {
1745 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1747 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1751 mark.position = (*i).frame;
1752 marks.push_back (mark);
1761 bbt_nmarks = bbt_bars + 2;
1762 for (n = 0, i = begin; i != end && n < bbt_nmarks; ++i) {
1763 if ((*i).is_bar()) {
1764 if ((*i).bar % 4 == 1) {
1765 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1766 mark.style = ArdourCanvas::Ruler::Mark::Major;
1769 if ((*i).bar % 4 == 3) {
1770 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1772 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1776 mark.position = (*i).frame;
1777 marks.push_back (mark);
1787 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1789 _samples_ruler_interval = (upper - lower) / 5;
1793 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1796 framepos_t const ilower = (framepos_t) floor (lower);
1800 ArdourCanvas::Ruler::Mark mark;
1802 if (_session == 0) {
1807 for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1808 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1810 mark.position = pos;
1811 mark.style = ArdourCanvas::Ruler::Mark::Major;
1812 marks.push_back (mark);
1817 sample_to_clock_parts ( framepos_t sample,
1818 framepos_t sample_rate,
1832 hrs = left / (sample_rate * 60 * 60 * 1000);
1833 left -= hrs * sample_rate * 60 * 60 * 1000;
1834 mins = left / (sample_rate * 60 * 1000);
1835 left -= mins * sample_rate * 60 * 1000;
1836 secs = left / (sample_rate * 1000);
1837 left -= secs * sample_rate * 1000;
1838 millisecs = left / sample_rate;
1840 *millisecs_p = millisecs;
1849 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1854 if (_session == 0) {
1858 fr = _session->frame_rate() * 1000;
1860 /* to prevent 'flashing' */
1861 if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1867 framecnt_t const range = (upper - lower) * 1000;
1869 if (range < (fr / 50)) {
1870 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1871 minsec_ruler_scale = minsec_show_frames;
1872 minsec_mark_modulo = 10;
1873 } else if (range <= (fr / 10)) { /* 0-0.1 second */
1874 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1875 minsec_ruler_scale = minsec_show_frames;
1876 minsec_mark_modulo = 10;
1877 } else if (range <= (fr / 2)) { /* 0-0.5 second */
1878 minsec_mark_interval = fr / 100; /* show 1/100 seconds */
1879 minsec_ruler_scale = minsec_show_frames;
1880 minsec_mark_modulo = 100;
1881 } else if (range <= fr) { /* 0-1 second */
1882 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1883 minsec_ruler_scale = minsec_show_frames;
1884 minsec_mark_modulo = 200;
1885 } else if (range <= 2 * fr) { /* 1-2 seconds */
1886 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1887 minsec_ruler_scale = minsec_show_frames;
1888 minsec_mark_modulo = 500;
1889 } else if (range <= 8 * fr) { /* 2-5 seconds */
1890 minsec_mark_interval = fr / 5; /* show 2 seconds */
1891 minsec_ruler_scale = minsec_show_frames;
1892 minsec_mark_modulo = 1000;
1893 } else if (range <= 16 * fr) { /* 8-16 seconds */
1894 minsec_mark_interval = fr; /* show 1 seconds */
1895 minsec_ruler_scale = minsec_show_seconds;
1896 minsec_mark_modulo = 2;
1897 } else if (range <= 30 * fr) { /* 10-30 seconds */
1898 minsec_mark_interval = fr; /* show 1 seconds */
1899 minsec_ruler_scale = minsec_show_seconds;
1900 minsec_mark_modulo = 5;
1901 } else if (range <= 60 * fr) { /* 30-60 seconds */
1902 minsec_mark_interval = fr; /* show 1 seconds */
1903 minsec_ruler_scale = minsec_show_seconds;
1904 minsec_mark_modulo = 5;
1905 } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1906 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1907 minsec_ruler_scale = minsec_show_seconds;
1908 minsec_mark_modulo = 3;
1909 } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1910 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1911 minsec_ruler_scale = minsec_show_seconds;
1912 minsec_mark_modulo = 30;
1913 } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1914 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1915 minsec_ruler_scale = minsec_show_seconds;
1916 minsec_mark_modulo = 120;
1917 } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1918 minsec_mark_interval = 60 * fr; /* show 1 minute */
1919 minsec_ruler_scale = minsec_show_minutes;
1920 minsec_mark_modulo = 5;
1921 } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1922 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1923 minsec_ruler_scale = minsec_show_minutes;
1924 minsec_mark_modulo = 10;
1925 } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1926 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1927 minsec_ruler_scale = minsec_show_minutes;
1928 minsec_mark_modulo = 30;
1929 } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1930 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1931 minsec_ruler_scale = minsec_show_minutes;
1932 minsec_mark_modulo = 60;
1933 } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1934 minsec_mark_interval = 60 * 60 * fr; /* show 60 minutes */
1935 minsec_ruler_scale = minsec_show_hours;
1936 minsec_mark_modulo = 2;
1939 /* not possible if framepos_t is a 32 bit quantity */
1941 minsec_mark_interval = 4 * 60 * 60 * fr; /* show 4 hrs */
1943 minsec_nmarks = 2 + (range / minsec_mark_interval);
1947 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1951 long hrs, mins, secs, millisecs;
1954 ArdourCanvas::Ruler::Mark mark;
1956 if (_session == 0) {
1960 /* to prevent 'flashing' */
1961 if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1962 lower = lower - spacer;
1967 pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1968 switch (minsec_ruler_scale) {
1969 case minsec_show_seconds:
1970 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1971 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1972 if (secs % minsec_mark_modulo == 0) {
1974 mark.style = ArdourCanvas::Ruler::Mark::Major;
1976 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1978 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1981 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1984 mark.position = pos/1000.0;
1985 marks.push_back (mark);
1988 case minsec_show_minutes:
1989 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1990 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1991 if (mins % minsec_mark_modulo == 0) {
1993 mark.style = ArdourCanvas::Ruler::Mark::Major;
1995 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1997 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
2000 mark.style = ArdourCanvas::Ruler::Mark::Micro;
2003 mark.position = pos/1000.0;
2004 marks.push_back (mark);
2007 case minsec_show_hours:
2008 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
2009 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
2010 if (hrs % minsec_mark_modulo == 0) {
2011 mark.style = ArdourCanvas::Ruler::Mark::Major;
2012 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
2015 mark.style = ArdourCanvas::Ruler::Mark::Micro;
2018 mark.position = pos/1000.0;
2019 marks.push_back (mark);
2022 case minsec_show_frames:
2023 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
2024 sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
2025 if (millisecs % minsec_mark_modulo == 0) {
2026 if (millisecs == 0) {
2027 mark.style = ArdourCanvas::Ruler::Mark::Major;
2029 mark.style = ArdourCanvas::Ruler::Mark::Minor;
2031 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
2034 mark.style = ArdourCanvas::Ruler::Mark::Micro;
2037 mark.position = pos/1000.0;
2038 marks.push_back (mark);