a-fluidsynth: implement LV2_BANKPATCH__notify
[ardour.git] / gtk2_ardour / editor_rulers.cc
1 /*
2     Copyright (C) 2000 Paul Davis
3
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.
8
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.
13
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.
17
18 */
19
20 #ifdef WAF_BUILD
21 #include "gtk2ardour-config.h"
22 #endif
23
24 #include <cstdio> // for sprintf, grrr
25 #include <cmath>
26 #include <inttypes.h>
27
28 #include <string>
29
30 #include <gtk/gtkaction.h>
31
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"
37
38 #include "ardour/session.h"
39 #include "ardour/tempo.h"
40 #include "ardour/profile.h"
41
42 #include "gtkmm2ext/gtk_ui.h"
43 #include "gtkmm2ext/keyboard.h"
44
45 #include "ardour_ui.h"
46 #include "editor.h"
47 #include "editing.h"
48 #include "actions.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"
55
56 #include "pbd/i18n.h"
57
58 using namespace ARDOUR;
59 using namespace PBD;
60 using namespace Gtk;
61 using namespace Editing;
62
63 /* the order here must match the "metric" enums in editor.h */
64
65 class TimecodeMetric : public ArdourCanvas::Ruler::Metric
66 {
67     public:
68         TimecodeMetric (Editor* e) : _editor (e) {}
69
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);
72         }
73
74     private:
75         Editor* _editor;
76 };
77
78 class SamplesMetric : public ArdourCanvas::Ruler::Metric
79 {
80     public:
81         SamplesMetric (Editor* e) : _editor (e) {}
82
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);
85         }
86
87     private:
88         Editor* _editor;
89 };
90
91 class BBTMetric : public ArdourCanvas::Ruler::Metric
92 {
93     public:
94         BBTMetric (Editor* e) : _editor (e) {}
95
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);
98         }
99
100     private:
101         Editor* _editor;
102 };
103
104 class MinsecMetric : public ArdourCanvas::Ruler::Metric
105 {
106     public:
107         MinsecMetric (Editor* e) : _editor (e) {}
108
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);
111         }
112
113     private:
114         Editor* _editor;
115 };
116
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;
121
122 void
123 Editor::initialize_rulers ()
124 {
125         ruler_grabbed_widget = 0;
126
127         Pango::FontDescription font (UIConfiguration::instance().get_SmallerFont());
128
129         _timecode_metric = new TimecodeMetric (this);
130         _bbt_metric = new BBTMetric (this);
131         _minsec_metric = new MinsecMetric (this);
132         _samples_metric = new SamplesMetric (this);
133
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");
138         timecode_nmarks = 0;
139
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");
144
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");
149         minsec_nmarks = 0;
150
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");
155         timecode_nmarks = 0;
156
157         using namespace Box_Helpers;
158         BoxList & lab_children =  time_bars_vbox.children();
159
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));
171
172         /* 1 event handler to bind them all ... */
173
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));
178
179         visible_timebars = 0; /*this will be changed below */
180 }
181
182 bool
183 Editor::ruler_label_button_release (GdkEventButton* ev)
184 {
185         if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
186                 if (!ruler_dialog) {
187                         ruler_dialog = new RulerDialog ();
188                 }
189                 ruler_dialog->present ();
190         }
191
192         return true;
193 }
194
195 void
196 Editor::popup_ruler_menu (framepos_t where, ItemType t)
197 {
198         using namespace Menu_Helpers;
199
200         if (editor_ruler_menu == 0) {
201                 editor_ruler_menu = new Menu;
202                 editor_ruler_menu->set_name ("ArdourContextMenu");
203         }
204
205         // always build from scratch
206         MenuList& ruler_items = editor_ruler_menu->items();
207         editor_ruler_menu->set_name ("ArdourContextMenu");
208         ruler_items.clear();
209
210         switch (t) {
211         case MarkerBarItem:
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)));
215                 break;
216
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)));
221                 break;
222
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)));
226                 break;
227
228         case CdMarkerBarItem:
229                 // TODO
230                 ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true)));
231                 break;
232
233         case TempoBarItem:
234                 ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
235                 break;
236
237         case MeterBarItem:
238                 ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
239                 break;
240
241         case VideoBarItem:
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
245                  */
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);}
254
255                 ruler_items.push_back (SeparatorElem ());
256
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")));
260                 {
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));
264                 }
265
266                 ruler_items.push_back (SeparatorElem ());
267
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")));
271                 {
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);
275                         }
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));
278                 }
279                 break;
280
281         default:
282                 break;
283         }
284
285         if (!ruler_items.empty()) {
286                 editor_ruler_menu->popup (1, gtk_get_current_event_time());
287         }
288
289         no_ruler_shown_update = false;
290 }
291
292 void
293 Editor::store_ruler_visibility ()
294 {
295         XMLNode* node = new XMLNode(X_("RulerVisibility"));
296
297         node->set_property (X_("timecode"), ruler_timecode_action->get_active());
298         node->set_property (X_("bbt"), ruler_bbt_action->get_active());
299         node->set_property (X_("samples"), ruler_samples_action->get_active());
300         node->set_property (X_("minsec"), ruler_minsec_action->get_active());
301         node->set_property (X_("tempo"), ruler_tempo_action->get_active());
302         node->set_property (X_("meter"), ruler_meter_action->get_active());
303         node->set_property (X_("marker"), ruler_marker_action->get_active());
304         node->set_property (X_("rangemarker"), ruler_range_action->get_active());
305         node->set_property (X_("transportmarker"), ruler_loop_punch_action->get_active());
306         node->set_property (X_("cdmarker"), ruler_cd_marker_action->get_active());
307         node->set_property (X_("videotl"), ruler_video_action->get_active());
308
309         _session->add_extra_xml (*node);
310         _session->set_dirty ();
311 }
312
313 void
314 Editor::restore_ruler_visibility ()
315 {
316         XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
317
318         no_ruler_shown_update = true;
319
320         bool yn;
321         if (node) {
322                 if (node->get_property ("timecode", yn)) {
323                         ruler_timecode_action->set_active (yn);
324                 }
325                 if (node->get_property ("bbt", yn)) {
326                         ruler_bbt_action->set_active (yn);
327                 }
328                 if (node->get_property ("samples", yn)) {
329                         ruler_samples_action->set_active (yn);
330                 }
331                 if (node->get_property ("minsec", yn)) {
332                         ruler_minsec_action->set_active (yn);
333                 }
334                 if (node->get_property ("tempo", yn)) {
335                         ruler_tempo_action->set_active (yn);
336                 }
337                 if (node->get_property ("meter", yn)) {
338                         ruler_meter_action->set_active (yn);
339                 }
340                 if (node->get_property ("marker", yn)) {
341                         ruler_marker_action->set_active (yn);
342                 }
343                 if (node->get_property ("rangemarker", yn)) {
344                         ruler_range_action->set_active (yn);
345                 }
346                 if (node->get_property ("transportmarker", yn)) {
347                         ruler_loop_punch_action->set_active (yn);
348                 }
349
350                 if (node->get_property ("cdmarker", yn)) {
351                                 ruler_cd_marker_action->set_active (yn);
352                 } else {
353                         // this _session doesn't yet know about the cdmarker ruler
354                         // as a benefit to the user who doesn't know the feature exists, show the ruler if
355                         // any cd marks exist
356                         ruler_cd_marker_action->set_active (false);
357                         const Locations::LocationList & locs = _session->locations()->list();
358                         for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
359                                 if ((*i)->is_cd_marker()) {
360                                         ruler_cd_marker_action->set_active (true);
361                                         break;
362                                 }
363                         }
364                 }
365
366                 if (node->get_property ("videotl", yn)) {
367                         ruler_video_action->set_active (yn);
368                 }
369
370         }
371
372         no_ruler_shown_update = false;
373         update_ruler_visibility ();
374 }
375
376 void
377 Editor::update_ruler_visibility ()
378 {
379         int visible_timebars = 0;
380
381         if (no_ruler_shown_update) {
382                 return;
383         }
384
385         /* the order of the timebars is fixed, so we have to go through each one
386          * and adjust its position depending on what is shown.
387          *
388          * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
389          * loop/punch, cd markers, location markers
390          */
391
392         double tbpos = 0.0;
393         double tbgpos = 0.0;
394         double old_unit_pos;
395
396 #ifdef __APPLE__
397         /* gtk update probs require this (damn) */
398         meter_label.hide();
399         tempo_label.hide();
400         range_mark_label.hide();
401         transport_mark_label.hide();
402         cd_mark_label.hide();
403         mark_label.hide();
404         videotl_label.hide();
405 #endif
406
407         if (ruler_minsec_action->get_active()) {
408                 old_unit_pos = minsec_ruler->position().y;
409                 if (tbpos != old_unit_pos) {
410                         minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
411                 }
412                 minsec_ruler->show();
413                 minsec_label.show();
414                 tbpos += timebar_height;
415                 tbgpos += timebar_height;
416                 visible_timebars++;
417         } else {
418                 minsec_ruler->hide();
419                 minsec_label.hide();
420         }
421
422         if (ruler_timecode_action->get_active()) {
423                 old_unit_pos = timecode_ruler->position().y;
424                 if (tbpos != old_unit_pos) {
425                         timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
426                 }
427                 timecode_ruler->show();
428                 timecode_label.show();
429                 tbpos += timebar_height;
430                 tbgpos += timebar_height;
431                 visible_timebars++;
432         } else {
433                 timecode_ruler->hide();
434                 timecode_label.hide();
435         }
436
437         if (ruler_samples_action->get_active()) {
438                 old_unit_pos = samples_ruler->position().y;
439                 if (tbpos != old_unit_pos) {
440                         samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
441                 }
442                 samples_ruler->show();
443                 samples_label.show();
444                 tbpos += timebar_height;
445                 tbgpos += timebar_height;
446                 visible_timebars++;
447         } else {
448                 samples_ruler->hide();
449                 samples_label.hide();
450         }
451
452         if (ruler_bbt_action->get_active()) {
453                 old_unit_pos = bbt_ruler->position().y;
454                 if (tbpos != old_unit_pos) {
455                         bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
456                 }
457                 bbt_ruler->show();
458                 bbt_label.show();
459                 tbpos += timebar_height;
460                 tbgpos += timebar_height;
461                 visible_timebars++;
462         } else {
463                 bbt_ruler->hide();
464                 bbt_label.hide();
465         }
466
467         if (ruler_meter_action->get_active()) {
468                 old_unit_pos = meter_group->position().y;
469                 if (tbpos != old_unit_pos) {
470                         meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
471                 }
472                 meter_group->show();
473                 meter_label.show();
474                 tbpos += timebar_height;
475                 tbgpos += timebar_height;
476                 visible_timebars++;
477         } else {
478                 meter_group->hide();
479                 meter_label.hide();
480         }
481
482         if (ruler_tempo_action->get_active()) {
483                 old_unit_pos = tempo_group->position().y;
484                 if (tbpos != old_unit_pos) {
485                         tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
486                 }
487                 tempo_group->show();
488                 tempo_label.show();
489                 tbpos += timebar_height;
490                 tbgpos += timebar_height;
491                 visible_timebars++;
492         } else {
493                 tempo_group->hide();
494                 tempo_label.hide();
495         }
496
497         if (ruler_range_action->get_active()) {
498                 old_unit_pos = range_marker_group->position().y;
499                 if (tbpos != old_unit_pos) {
500                         range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
501                 }
502                 range_marker_group->show();
503                 range_mark_label.show();
504
505                 tbpos += timebar_height;
506                 tbgpos += timebar_height;
507                 visible_timebars++;
508         } else {
509                 range_marker_group->hide();
510                 range_mark_label.hide();
511         }
512
513         if (ruler_loop_punch_action->get_active()) {
514                 old_unit_pos = transport_marker_group->position().y;
515                 if (tbpos != old_unit_pos) {
516                         transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
517                 }
518                 transport_marker_group->show();
519                 transport_mark_label.show();
520                 tbpos += timebar_height;
521                 tbgpos += timebar_height;
522                 visible_timebars++;
523         } else {
524                 transport_marker_group->hide();
525                 transport_mark_label.hide();
526         }
527
528         if (ruler_cd_marker_action->get_active()) {
529                 old_unit_pos = cd_marker_group->position().y;
530                 if (tbpos != old_unit_pos) {
531                         cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
532                 }
533                 cd_marker_group->show();
534                 cd_mark_label.show();
535                 tbpos += timebar_height;
536                 tbgpos += timebar_height;
537                 visible_timebars++;
538                 // make sure all cd markers show up in their respective places
539                 update_cd_marker_display();
540         } else {
541                 cd_marker_group->hide();
542                 cd_mark_label.hide();
543                 // make sure all cd markers show up in their respective places
544                 update_cd_marker_display();
545         }
546
547         if (ruler_marker_action->get_active()) {
548                 old_unit_pos = marker_group->position().y;
549                 if (tbpos != old_unit_pos) {
550                         marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
551                 }
552                 marker_group->show();
553                 mark_label.show();
554                 tbpos += timebar_height;
555                 tbgpos += timebar_height;
556                 visible_timebars++;
557         } else {
558                 marker_group->hide();
559                 mark_label.hide();
560         }
561
562         if (ruler_video_action->get_active()) {
563                 old_unit_pos = videotl_group->position().y;
564                 if (tbpos != old_unit_pos) {
565                         videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
566                 }
567                 videotl_group->show();
568                 videotl_label.show();
569                 tbpos += timebar_height * videotl_bar_height;
570                 tbgpos += timebar_height * videotl_bar_height;
571                 visible_timebars+=videotl_bar_height;
572                 queue_visual_videotimeline_update();
573         } else {
574                 videotl_group->hide();
575                 videotl_label.hide();
576                 update_video_timeline(true);
577         }
578
579         time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
580
581         /* move hv_scroll_group (trackviews) to the end of the timebars
582          */
583
584         hv_scroll_group->set_y_position (timebar_height * visible_timebars);
585
586         compute_fixed_ruler_scale ();
587         update_fixed_rulers();
588         redisplay_tempo (false);
589
590         /* Changing ruler visibility means that any lines on markers might need updating */
591         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
592                 i->second->setup_lines ();
593         }
594 }
595
596 void
597 Editor::update_just_timecode ()
598 {
599         ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
600
601         if (_session == 0) {
602                 return;
603         }
604
605         framepos_t rightmost_frame = leftmost_frame + current_page_samples();
606
607         if (ruler_timecode_action->get_active()) {
608                 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
609         }
610 }
611
612 void
613 Editor::compute_fixed_ruler_scale ()
614 {
615         if (_session == 0) {
616                 return;
617         }
618
619         if (ruler_timecode_action->get_active()) {
620                 set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
621         }
622
623         if (ruler_minsec_action->get_active()) {
624                 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
625         }
626
627         if (ruler_samples_action->get_active()) {
628                 set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
629         }
630 }
631
632 void
633 Editor::update_fixed_rulers ()
634 {
635         framepos_t rightmost_frame;
636
637         if (_session == 0) {
638                 return;
639         }
640
641         compute_fixed_ruler_scale ();
642
643         _timecode_metric->units_per_pixel = samples_per_pixel;
644         _samples_metric->units_per_pixel = samples_per_pixel;
645         _minsec_metric->units_per_pixel = samples_per_pixel;
646
647         rightmost_frame = leftmost_frame + current_page_samples();
648
649         /* these force a redraw, which in turn will force execution of the metric callbacks
650            to compute the relevant ticks to display.
651         */
652
653         if (ruler_timecode_action->get_active()) {
654                 timecode_ruler->set_range (leftmost_frame, rightmost_frame);
655         }
656
657         if (ruler_samples_action->get_active()) {
658                 samples_ruler->set_range (leftmost_frame, rightmost_frame);
659         }
660
661         if (ruler_minsec_action->get_active()) {
662                 minsec_ruler->set_range (leftmost_frame, rightmost_frame);
663         }
664 }
665
666 void
667 Editor::update_tempo_based_rulers ()
668 {
669         if (_session == 0) {
670                 return;
671         }
672
673         _bbt_metric->units_per_pixel = samples_per_pixel;
674
675         if (ruler_bbt_action->get_active()) {
676                 bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
677         }
678 }
679
680
681 void
682 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
683 {
684         using namespace std;
685
686         framepos_t spacer;
687         framepos_t fr;
688
689         if (_session == 0) {
690                 return;
691         }
692
693         fr = _session->frame_rate();
694
695         if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
696                 lower = lower - spacer;
697         } else {
698                 lower = 0;
699         }
700
701         upper = upper + spacer;
702         framecnt_t const range = upper - lower;
703
704         if (range < (2 * _session->samples_per_timecode_frame())) { /* 0 - 2 frames */
705                 timecode_ruler_scale = timecode_show_bits;
706                 timecode_mark_modulo = 20;
707                 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
708         } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
709                 timecode_ruler_scale = timecode_show_frames;
710                 timecode_mark_modulo = 1;
711                 timecode_nmarks = 2 + (range / (framepos_t)_session->samples_per_timecode_frame());
712         } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
713                 timecode_ruler_scale = timecode_show_frames;
714                 timecode_mark_modulo = 2;
715                 timecode_nmarks = 2 + (range / (framepos_t)_session->samples_per_timecode_frame());
716         } else if (range <= fr) { /* 0.5-1 second */
717                 timecode_ruler_scale = timecode_show_frames;
718                 timecode_mark_modulo = 5;
719                 timecode_nmarks = 2 + (range / (framepos_t)_session->samples_per_timecode_frame());
720         } else if (range <= 2 * fr) { /* 1-2 seconds */
721                 timecode_ruler_scale = timecode_show_frames;
722                 timecode_mark_modulo = 10;
723                 timecode_nmarks = 2 + (range / (framepos_t)_session->samples_per_timecode_frame());
724         } else if (range <= 8 * fr) { /* 2-8 seconds */
725                 timecode_ruler_scale = timecode_show_seconds;
726                 timecode_mark_modulo = 1;
727                 timecode_nmarks = 2 + (range / fr);
728         } else if (range <= 16 * fr) { /* 8-16 seconds */
729                 timecode_ruler_scale = timecode_show_seconds;
730                 timecode_mark_modulo = 2;
731                 timecode_nmarks = 2 + (range / fr);
732         } else if (range <= 30 * fr) { /* 16-30 seconds */
733                 timecode_ruler_scale = timecode_show_seconds;
734                 timecode_mark_modulo = 5;
735                 timecode_nmarks = 2 + (range / fr);
736         } else if (range <= 60 * fr) { /* 30-60 seconds */
737                 timecode_ruler_scale = timecode_show_seconds;
738                 timecode_mark_modulo = 5;
739                 timecode_nmarks = 2 + (range / fr);
740         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
741                 timecode_ruler_scale = timecode_show_seconds;
742                 timecode_mark_modulo = 15;
743                 timecode_nmarks = 2 + (range / fr);
744         } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
745                 timecode_ruler_scale = timecode_show_seconds;
746                 timecode_mark_modulo = 30;
747                 timecode_nmarks = 2 + (range / fr);
748         } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
749                 timecode_ruler_scale = timecode_show_minutes;
750                 timecode_mark_modulo = 2;
751                 timecode_nmarks = 2 + 10;
752         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
753                 timecode_ruler_scale = timecode_show_minutes;
754                 timecode_mark_modulo = 5;
755                 timecode_nmarks = 2 + 30;
756         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
757                 timecode_ruler_scale = timecode_show_minutes;
758                 timecode_mark_modulo = 10;
759                 timecode_nmarks = 2 + 60;
760         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
761                 timecode_ruler_scale = timecode_show_minutes;
762                 timecode_mark_modulo = 30;
763                 timecode_nmarks = 2 + (60 * 4);
764         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
765                 timecode_ruler_scale = timecode_show_hours;
766                 timecode_mark_modulo = 1;
767                 timecode_nmarks = 2 + 8;
768         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
769                 timecode_ruler_scale = timecode_show_hours;
770                 timecode_mark_modulo = 1;
771                 timecode_nmarks = 2 + 24;
772         } else {
773
774                 const framecnt_t hours_in_range = range / (60 * 60 * fr);
775                 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
776
777                 /* Normally we do not need to know anything about the width of the canvas
778                    to set the ruler scale, because the caller has already determined
779                    the width and set lower + upper arguments to this function to match that.
780
781                    But in this case, where the range defined by lower and uppper can vary
782                    substantially (basically anything from 24hrs+ to several billion years)
783                    trying to decide which tick marks to show does require us to know
784                    about the available width.
785                 */
786
787                 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
788                 timecode_ruler_scale = timecode_show_many_hours;
789                 timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
790         }
791 }
792
793 void
794 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
795 {
796         framepos_t pos;
797         framecnt_t spacer;
798         Timecode::Time timecode;
799         gchar buf[16];
800         gint n;
801         ArdourCanvas::Ruler::Mark mark;
802
803         if (_session == 0) {
804                 return;
805         }
806
807         if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
808                 lower = lower - spacer;
809         } else {
810                 lower = 0;
811         }
812
813         pos = (framecnt_t) floor (lower);
814
815         switch (timecode_ruler_scale) {
816         case timecode_show_bits:
817                 // Find timecode time of this sample (pos) with subframe accuracy
818                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
819                 for (n = 0; n < timecode_nmarks; n++) {
820                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
821                         if ((timecode.subframes % timecode_mark_modulo) == 0) {
822                                 if (timecode.subframes == 0) {
823                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
824                                         snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
825                                 } else {
826                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
827                                         snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
828                                 }
829                         } else {
830                                 snprintf (buf, sizeof(buf)," ");
831                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
832                         }
833                         mark.label = buf;
834                         mark.position = pos;
835                         marks.push_back (mark);
836                         // Increment subframes by one
837                         Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
838                 }
839                 break;
840
841         case timecode_show_frames:
842                 // Find timecode time of this sample (pos)
843                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
844                 // Go to next whole frame down
845                 Timecode::frames_floor( timecode );
846                 for (n = 0; n < timecode_nmarks; n++) {
847                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
848                         if ((timecode.frames % timecode_mark_modulo) == 0) {
849                                 if (timecode.frames == 0) {
850                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
851                                 } else {
852                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
853                                 }
854                                 mark.position = pos;
855                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
856                         } else {
857                                 snprintf (buf, sizeof(buf)," ");
858                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
859                                 mark.position = pos;
860                         }
861                         mark.label = buf;
862                         marks.push_back (mark);
863                         Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
864                 }
865                 break;
866
867         case timecode_show_seconds:
868                 // Find timecode time of this sample (pos)
869                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
870                 // Go to next whole second down
871                 Timecode::seconds_floor( timecode );
872                 for (n = 0; n < timecode_nmarks; n++) {
873                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
874                         if ((timecode.seconds % timecode_mark_modulo) == 0) {
875                                 if (timecode.seconds == 0) {
876                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
877                                         mark.position = pos;
878                                 } else {
879                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
880                                         mark.position = pos;
881                                 }
882                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
883                         } else {
884                                 snprintf (buf, sizeof(buf)," ");
885                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
886                                 mark.position = pos;
887                         }
888                         mark.label = buf;
889                         marks.push_back (mark);
890                         Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
891                 }
892                 break;
893
894         case timecode_show_minutes:
895                 //Find timecode time of this sample (pos)
896                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
897                 // Go to next whole minute down
898                 Timecode::minutes_floor( timecode );
899                 for (n = 0; n < timecode_nmarks; n++) {
900                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
901                         if ((timecode.minutes % timecode_mark_modulo) == 0) {
902                                 if (timecode.minutes == 0) {
903                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
904                                 } else {
905                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
906                                 }
907                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
908                         } else {
909                                 snprintf (buf, sizeof(buf)," ");
910                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
911                         }
912                         mark.label = buf;
913                         mark.position = pos;
914                         marks.push_back (mark);
915                         Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
916                 }
917                 break;
918         case timecode_show_hours:
919                 // Find timecode time of this sample (pos)
920                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
921                 // Go to next whole hour down
922                 Timecode::hours_floor( timecode );
923                 for (n = 0; n < timecode_nmarks; n++) {
924                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
925                         if ((timecode.hours % timecode_mark_modulo) == 0) {
926                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
927                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
928                         } else {
929                                 snprintf (buf, sizeof(buf)," ");
930                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
931                         }
932                         mark.label = buf;
933                         mark.position = pos;
934                         marks.push_back (mark);
935                         Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
936                 }
937                 break;
938         case timecode_show_many_hours:
939                 // Find timecode time of this sample (pos)
940                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
941                 // Go to next whole hour down
942                 Timecode::hours_floor (timecode);
943
944                 for (n = 0; n < timecode_nmarks; ) {
945                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
946                         if ((timecode.hours % timecode_mark_modulo) == 0) {
947                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
948                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
949                                 mark.label = buf;
950                                 mark.position = pos;
951                                 marks.push_back (mark);
952                                 ++n;
953                         }
954                         /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
955                          * and doing it 1 hour at a time is just stupid (and slow).
956                          */
957                         timecode.hours += timecode_mark_modulo - (timecode.hours % timecode_mark_modulo);
958                 }
959                 break;
960         }
961 }
962
963 void
964 Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper)
965 {
966         if (_session == 0) {
967                 return;
968         }
969
970         std::vector<TempoMap::BBTPoint>::const_iterator i;
971         Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
972         double floor_lower_beat = floor(max (0.0, _session->tempo_map().beat_at_frame (lower)));
973
974         if (floor_lower_beat < 0.0) {
975                 floor_lower_beat = 0.0;
976         }
977
978         const framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor_lower_beat);
979         const framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (max (0.0, _session->tempo_map().beat_at_frame (upper))) + 1.0);
980
981         _session->bbt_time (beat_before_lower_pos, lower_beat);
982         _session->bbt_time (beat_after_upper_pos, upper_beat);
983         uint32_t beats = 0;
984
985         bbt_accent_modulo = 1;
986         bbt_bar_helper_on = false;
987         bbt_bars = 0;
988         bbt_nmarks = 1;
989
990         bbt_ruler_scale =  bbt_show_many;
991
992         switch (_snap_type) {
993         case SnapToBeatDiv2:
994                 bbt_beat_subdivision = 2;
995                 break;
996         case SnapToBeatDiv3:
997                 bbt_beat_subdivision = 3;
998                 break;
999         case SnapToBeatDiv4:
1000                 bbt_beat_subdivision = 4;
1001                 break;
1002         case SnapToBeatDiv5:
1003                 bbt_beat_subdivision = 5;
1004                 bbt_accent_modulo = 2; // XXX YIKES
1005                 break;
1006         case SnapToBeatDiv6:
1007                 bbt_beat_subdivision = 6;
1008                 bbt_accent_modulo = 2; // XXX YIKES
1009                 break;
1010         case SnapToBeatDiv7:
1011                 bbt_beat_subdivision = 7;
1012                 bbt_accent_modulo = 2; // XXX YIKES
1013                 break;
1014         case SnapToBeatDiv8:
1015                 bbt_beat_subdivision = 8;
1016                 bbt_accent_modulo = 2;
1017                 break;
1018         case SnapToBeatDiv10:
1019                 bbt_beat_subdivision = 10;
1020                 bbt_accent_modulo = 2; // XXX YIKES
1021                 break;
1022         case SnapToBeatDiv12:
1023                 bbt_beat_subdivision = 12;
1024                 bbt_accent_modulo = 3;
1025                 break;
1026         case SnapToBeatDiv14:
1027                 bbt_beat_subdivision = 14;
1028                 bbt_accent_modulo = 3; // XXX YIKES!
1029                 break;
1030         case SnapToBeatDiv16:
1031                 bbt_beat_subdivision = 16;
1032                 bbt_accent_modulo = 4;
1033                 break;
1034         case SnapToBeatDiv20:
1035                 bbt_beat_subdivision = 20;
1036                 bbt_accent_modulo = 5;
1037                 break;
1038         case SnapToBeatDiv24:
1039                 bbt_beat_subdivision = 24;
1040                 bbt_accent_modulo = 6;
1041                 break;
1042         case SnapToBeatDiv28:
1043                 bbt_beat_subdivision = 28;
1044                 bbt_accent_modulo = 7;
1045                 break;
1046         case SnapToBeatDiv32:
1047                 bbt_beat_subdivision = 32;
1048                 bbt_accent_modulo = 8;
1049                 break;
1050         case SnapToBeatDiv64:
1051                 bbt_beat_subdivision = 64;
1052                 bbt_accent_modulo = 8;
1053                 break;
1054         case SnapToBeatDiv128:
1055                 bbt_beat_subdivision = 128;
1056                 bbt_accent_modulo = 8;
1057                 break;
1058         default:
1059                 bbt_beat_subdivision = 4;
1060                 break;
1061         }
1062
1063         const double ceil_upper_beat = floor (max (0.0, _session->tempo_map().beat_at_frame (upper))) + 1.0;
1064         if (ceil_upper_beat == floor_lower_beat) {
1065                 return;
1066         }
1067
1068         bbt_bars = _session->tempo_map().bbt_at_beat (ceil_upper_beat).bars - _session->tempo_map().bbt_at_beat (floor_lower_beat).bars;
1069
1070         beats = (ceil_upper_beat - floor_lower_beat);// - bbt_bars;  possible thinko; this fixes the problem (for me) where measure lines alternately appear&disappear while playing at certain zoom scales
1071         double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / 5.0;
1072
1073         /* Only show the bar helper if there aren't many bars on the screen */
1074         if ((bbt_bars < 2) || (beats < 5)) {
1075                 bbt_bar_helper_on = true;
1076         }
1077
1078         if (beat_density > 8192) {
1079                 bbt_ruler_scale = bbt_show_many;
1080         } else if (beat_density > 1024) {
1081                 bbt_ruler_scale = bbt_show_64;
1082         } else if (beat_density > 512) {
1083                 bbt_ruler_scale = bbt_show_16;
1084         } else if (beat_density > 128) {
1085                 bbt_ruler_scale = bbt_show_4;
1086         } else if (beat_density > 16) {
1087                 bbt_ruler_scale =  bbt_show_1;
1088         } else if (beat_density > 2) {
1089                 bbt_ruler_scale =  bbt_show_beats;
1090         } else  if (beat_density > 0.5) {
1091                 bbt_ruler_scale =  bbt_show_ticks;
1092         } else {
1093                 bbt_ruler_scale =  bbt_show_ticks_detail;
1094         }
1095
1096         if ((bbt_ruler_scale == bbt_show_ticks_detail) && beats < 3) {
1097                 bbt_ruler_scale =  bbt_show_ticks_super_detail;
1098         }
1099 }
1100
1101 static void
1102 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1103 {
1104         ArdourCanvas::Ruler::Mark copy = marks.back();
1105         copy.label = newlabel;
1106         marks.pop_back ();
1107         marks.push_back (copy);
1108 }
1109
1110 void
1111 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1112 {
1113         if (_session == 0) {
1114                 return;
1115         }
1116
1117         std::vector<TempoMap::BBTPoint>::const_iterator i;
1118
1119         char buf[64];
1120         gint  n = 0;
1121         framepos_t pos;
1122         Timecode::BBT_Time next_beat;
1123         uint32_t beats = 0;
1124         uint32_t tick = 0;
1125         uint32_t skip;
1126         uint32_t t;
1127         double bbt_position_of_helper;
1128         bool i_am_accented = false;
1129         bool helper_active = false;
1130         ArdourCanvas::Ruler::Mark mark;
1131
1132         std::vector<TempoMap::BBTPoint> grid;
1133
1134         compute_current_bbt_points (grid, lower, upper);
1135
1136         if (distance (grid.begin(), grid.end()) == 0) {
1137                 return;
1138         }
1139
1140         switch (bbt_ruler_scale) {
1141
1142         case bbt_show_beats:
1143
1144                 beats = distance (grid.begin(), grid.end());
1145                 bbt_nmarks = beats + 2;
1146
1147                 mark.label = "";
1148                 mark.position = lower;
1149                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1150                 marks.push_back (mark);
1151
1152                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1153
1154                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1155                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1156                                 edit_last_mark_label (marks, buf);
1157                         } else {
1158
1159                                 if ((*i).is_bar()) {
1160                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1161                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1162                                 } else if (((*i).beat % 2 == 1)) {
1163                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1164                                         buf[0] = '\0';
1165                                 } else {
1166                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1167                                         buf[0] = '\0';
1168                                 }
1169                                 mark.label = buf;
1170                                 mark.position = (*i).frame;
1171                                 marks.push_back (mark);
1172                                 n++;
1173                         }
1174                 }
1175                 break;
1176
1177         case bbt_show_ticks:
1178
1179                 beats = distance (grid.begin(), grid.end());
1180                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1181
1182                 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1183
1184                 // could do marks.assign() here to preallocate
1185
1186                 mark.label = "";
1187                 mark.position = lower;
1188                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1189                 marks.push_back (mark);
1190
1191                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1192
1193                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1194                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1195                                 edit_last_mark_label (marks, buf);
1196                                 helper_active = true;
1197                         } else {
1198
1199                                 if ((*i).is_bar()) {
1200                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1201                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1202                                 } else {
1203                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1204                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1205                                 }
1206                                 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1207                                         buf[0] = '\0';
1208                                 }
1209                                 mark.label =  buf;
1210                                 mark.position = (*i).frame;
1211                                 marks.push_back (mark);
1212                                 n++;
1213                         }
1214
1215                         /* Add the tick marks */
1216                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1217                         tick = skip; // the first non-beat tick
1218                         t = 0;
1219                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1220
1221                                 next_beat.beats = (*i).beat;
1222                                 next_beat.bars = (*i).bar;
1223                                 next_beat.ticks = tick;
1224                                 pos = _session->tempo_map().frame_at_bbt (next_beat);
1225
1226                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1227                                         i_am_accented = true;
1228                                 }
1229                                 mark.label = "";
1230                                 mark.position = pos;
1231
1232                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1233                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1234                                 } else {
1235                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1236                                 }
1237                                 i_am_accented = false;
1238                                 marks.push_back (mark);
1239
1240                                 tick += skip;
1241                                 ++t;
1242                                 ++n;
1243                         }
1244                 }
1245
1246           break;
1247
1248         case bbt_show_ticks_detail:
1249
1250                 beats = distance (grid.begin(), grid.end());
1251                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1252
1253                 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1254
1255                 mark.label = "";
1256                 mark.position = lower;
1257                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1258                 marks.push_back (mark);
1259
1260                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1261
1262                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1263                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1264                                 edit_last_mark_label (marks, buf);
1265                                 helper_active = true;
1266                         } else {
1267
1268                                 if ((*i).is_bar()) {
1269                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1270                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1271                                 } else {
1272                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1273                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1274                                 }
1275                                 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1276                                         buf[0] = '\0';
1277                                 }
1278                                 mark.label =  buf;
1279                                 mark.position = (*i).frame;
1280                                 marks.push_back (mark);
1281                                 n++;
1282                         }
1283
1284                         /* Add the tick marks */
1285                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1286                         tick = skip; // the first non-beat tick
1287
1288                         t = 0;
1289                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1290
1291                                 next_beat.beats = (*i).beat;
1292                                 next_beat.bars = (*i).bar;
1293                                 next_beat.ticks = tick;
1294                                 pos = _session->tempo_map().frame_at_bbt (next_beat);
1295
1296                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1297                                         i_am_accented = true;
1298                                 }
1299                                 if (i_am_accented && (pos > bbt_position_of_helper)){
1300                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1301                                 } else {
1302                                         buf[0] = '\0';
1303                                 }
1304
1305                                 mark.label = buf;
1306                                 mark.position = pos;
1307
1308                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1309                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1310                                 } else {
1311                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1312                                 }
1313                                 i_am_accented = false;
1314                                 marks.push_back (mark);
1315
1316                                 tick += skip;
1317                                 ++t;
1318                                 ++n;
1319                         }
1320                 }
1321
1322           break;
1323
1324         case bbt_show_ticks_super_detail:
1325
1326                 beats = distance (grid.begin(), grid.end());
1327                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1328
1329                 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1330
1331                 mark.label = "";
1332                 mark.position = lower;
1333                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1334                 marks.push_back (mark);
1335
1336                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1337
1338                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1339                                   snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1340                                   edit_last_mark_label (marks, buf);
1341                                   helper_active = true;
1342                         } else {
1343
1344                                   if ((*i).is_bar()) {
1345                                           mark.style = ArdourCanvas::Ruler::Mark::Major;
1346                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1347                                   } else {
1348                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1349                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1350                                   }
1351                                   if (((*i).frame < bbt_position_of_helper) && helper_active) {
1352                                           buf[0] = '\0';
1353                                   }
1354                                   mark.label =  buf;
1355                                   mark.position = (*i).frame;
1356                                   marks.push_back (mark);
1357                                   n++;
1358                         }
1359
1360                         /* Add the tick marks */
1361                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1362
1363                         next_beat.beats = (*i).beat;
1364                         next_beat.bars = (*i).bar;
1365                         tick = skip; // the first non-beat tick
1366                         t = 0;
1367                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1368
1369                                 next_beat.ticks = tick;
1370                                 pos = _session->tempo_map().frame_at_bbt (next_beat);
1371                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1372                                         i_am_accented = true;
1373                                 }
1374
1375                                 if (pos > bbt_position_of_helper) {
1376                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1377                                 } else {
1378                                         buf[0] = '\0';
1379                                 }
1380
1381                                 mark.label = buf;
1382                                 mark.position = pos;
1383
1384                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1385                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1386                                 } else {
1387                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1388                                 }
1389                                 i_am_accented = false;
1390                                 marks.push_back (mark);
1391
1392                                 tick += skip;
1393                                 ++t;
1394                                 ++n;
1395                         }
1396                 }
1397
1398           break;
1399
1400         case bbt_show_many:
1401                 bbt_nmarks = 1;
1402                 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1403                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1404                 mark.label = buf;
1405                 mark.position = lower;
1406                 marks.push_back (mark);
1407                 break;
1408
1409         case bbt_show_64:
1410                         bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1411                         for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1412                                 if ((*i).is_bar()) {
1413                                         if ((*i).bar % 64 == 1) {
1414                                                 if ((*i).bar % 256 == 1) {
1415                                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1416                                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1417                                                 } else {
1418                                                         buf[0] = '\0';
1419                                                         if ((*i).bar % 256 == 129)  {
1420                                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1421                                                         } else {
1422                                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1423                                                         }
1424                                                 }
1425                                                 mark.label = buf;
1426                                                 mark.position = (*i).frame;
1427                                                 marks.push_back (mark);
1428                                                 ++n;
1429                                         }
1430                                 }
1431                         }
1432                         break;
1433
1434         case bbt_show_16:
1435                 bbt_nmarks = (bbt_bars / 16) + 1;
1436                 for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1437                         if ((*i).is_bar()) {
1438                           if ((*i).bar % 16 == 1) {
1439                                 if ((*i).bar % 64 == 1) {
1440                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1441                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1442                                 } else {
1443                                         buf[0] = '\0';
1444                                         if ((*i).bar % 64 == 33)  {
1445                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1446                                         } else {
1447                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1448                                         }
1449                                 }
1450                                 mark.label = buf;
1451                                 mark.position = (*i).frame;
1452                                 marks.push_back (mark);
1453                                 ++n;
1454                           }
1455                         }
1456                 }
1457           break;
1458
1459         case bbt_show_4:
1460                 bbt_nmarks = (bbt_bars / 4) + 1;
1461                 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1462                         if ((*i).is_bar()) {
1463                                 if ((*i).bar % 4 == 1) {
1464                                         if ((*i).bar % 16 == 1) {
1465                                                 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1466                                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1467                                         } else {
1468                                                 buf[0] = '\0';
1469                                                 if ((*i).bar % 16 == 9)  {
1470                                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1471                                                 } else {
1472                                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1473                                                 }
1474                                         }
1475                                         mark.label = buf;
1476                                         mark.position = (*i).frame;
1477                                         marks.push_back (mark);
1478                                         ++n;
1479                                 }
1480                         }
1481                 }
1482           break;
1483
1484         case bbt_show_1:
1485 //      default:
1486                 bbt_nmarks = bbt_bars + 2;
1487                 for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1488                         if ((*i).is_bar()) {
1489                           if ((*i).bar % 4 == 1) {
1490                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1491                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1492                           } else {
1493                                   buf[0] = '\0';
1494                                   if ((*i).bar % 4 == 3)  {
1495                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1496                                   } else {
1497                                           mark.style = ArdourCanvas::Ruler::Mark::Micro;
1498                                   }
1499                           }
1500                           mark.label = buf;
1501                           mark.position = (*i).frame;
1502                           marks.push_back (mark);
1503                           ++n;
1504                         }
1505                 }
1506                 break;
1507
1508         }
1509 }
1510
1511 void
1512 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1513 {
1514         _samples_ruler_interval = (upper - lower) / 5;
1515 }
1516
1517 void
1518 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1519 {
1520         framepos_t pos;
1521         framepos_t const ilower = (framepos_t) floor (lower);
1522         gchar buf[16];
1523         gint nmarks;
1524         gint n;
1525         ArdourCanvas::Ruler::Mark mark;
1526
1527         if (_session == 0) {
1528                 return;
1529         }
1530
1531         nmarks = 5;
1532         for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1533                 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1534                 mark.label = buf;
1535                 mark.position = pos;
1536                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1537                 marks.push_back (mark);
1538         }
1539 }
1540
1541 static void
1542 sample_to_clock_parts ( framepos_t sample,
1543                         framepos_t sample_rate,
1544                         long *hrs_p,
1545                         long *mins_p,
1546                         long *secs_p,
1547                         long *millisecs_p)
1548
1549 {
1550         framepos_t left;
1551         long hrs;
1552         long mins;
1553         long secs;
1554         long millisecs;
1555
1556         left = sample;
1557         hrs = left / (sample_rate * 60 * 60 * 1000);
1558         left -= hrs * sample_rate * 60 * 60 * 1000;
1559         mins = left / (sample_rate * 60 * 1000);
1560         left -= mins * sample_rate * 60 * 1000;
1561         secs = left / (sample_rate * 1000);
1562         left -= secs * sample_rate * 1000;
1563         millisecs = left / sample_rate;
1564
1565         *millisecs_p = millisecs;
1566         *secs_p = secs;
1567         *mins_p = mins;
1568         *hrs_p = hrs;
1569
1570         return;
1571 }
1572
1573 void
1574 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1575 {
1576         framepos_t fr = _session->frame_rate() * 1000;
1577         framepos_t spacer;
1578
1579         if (_session == 0) {
1580                 return;
1581         }
1582
1583
1584         /* to prevent 'flashing' */
1585         if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1586                 lower -= spacer;
1587         } else {
1588                 lower = 0;
1589         }
1590         upper += spacer;
1591         framecnt_t const range = (upper - lower) * 1000;
1592
1593         if (range <= (fr / 10)) { /* 0-0.1 second */
1594                 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1595                 minsec_ruler_scale = minsec_show_msecs;
1596                 minsec_mark_modulo = 10;
1597                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1598         } else if (range <= (fr / 2)) { /* 0-0.5 second */
1599                 minsec_mark_interval = fr / 100;  /* show 1/100 seconds */
1600                 minsec_ruler_scale = minsec_show_msecs;
1601                 minsec_mark_modulo = 100;
1602                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1603         } else if (range <= fr) { /* 0-1 second */
1604                 minsec_mark_interval = fr / 10;  /* show 1/10 seconds */
1605                 minsec_ruler_scale = minsec_show_msecs;
1606                 minsec_mark_modulo = 200;
1607                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1608         } else if (range <= 2 * fr) { /* 1-2 seconds */
1609                 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1610                 minsec_ruler_scale = minsec_show_msecs;
1611                 minsec_mark_modulo = 500;
1612                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1613         } else if (range <= 8 * fr) { /* 2-5 seconds */
1614                 minsec_mark_interval =  fr / 5; /* show 2 seconds */
1615                 minsec_ruler_scale = minsec_show_msecs;
1616                 minsec_mark_modulo = 1000;
1617                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1618         } else if (range <= 16 * fr) { /* 8-16 seconds */
1619                 minsec_mark_interval =  fr; /* show 1 seconds */
1620                 minsec_ruler_scale = minsec_show_seconds;
1621                 minsec_mark_modulo = 2;
1622                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1623         } else if (range <= 30 * fr) { /* 10-30 seconds */
1624                 minsec_mark_interval =  fr; /* show 1 seconds */
1625                 minsec_ruler_scale = minsec_show_seconds;
1626                 minsec_mark_modulo = 5;
1627                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1628         } else if (range <= 60 * fr) { /* 30-60 seconds */
1629                 minsec_mark_interval = fr; /* show 1 seconds */
1630                 minsec_ruler_scale = minsec_show_seconds;
1631                 minsec_mark_modulo = 5;
1632                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1633         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1634                 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1635                 minsec_ruler_scale = minsec_show_seconds;
1636                 minsec_mark_modulo = 3;
1637                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1638         } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1639                 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1640                 minsec_ruler_scale = minsec_show_seconds;
1641                 minsec_mark_modulo = 30;
1642                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1643         } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1644                 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1645                 minsec_ruler_scale = minsec_show_seconds;
1646                 minsec_mark_modulo = 120;
1647                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1648         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1649                 minsec_mark_interval =  60 * fr; /* show 1 minute */
1650                 minsec_ruler_scale = minsec_show_minutes;
1651                 minsec_mark_modulo = 5;
1652                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1653         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1654                 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1655                 minsec_ruler_scale = minsec_show_minutes;
1656                 minsec_mark_modulo = 10;
1657                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1658         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1659                 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1660                 minsec_ruler_scale = minsec_show_minutes;
1661                 minsec_mark_modulo = 30;
1662                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1663         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1664                 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1665                 minsec_ruler_scale = minsec_show_minutes;
1666                 minsec_mark_modulo = 60;
1667                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1668         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1669                 minsec_mark_interval =  60 * 60 * fr; /* show 60 minutes */
1670                 minsec_ruler_scale = minsec_show_hours;
1671                 minsec_mark_modulo = 2;
1672                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1673         } else {
1674
1675                 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1676                 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1677
1678                 /* Normally we do not need to know anything about the width of the canvas
1679                    to set the ruler scale, because the caller has already determined
1680                    the width and set lower + upper arguments to this function to match that.
1681
1682                    But in this case, where the range defined by lower and uppper can vary
1683                    substantially (anything from 24hrs+ to several billion years)
1684                    trying to decide which tick marks to show does require us to know
1685                    about the available width.
1686                 */
1687
1688                 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1689                 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1690                 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1691                 minsec_ruler_scale = minsec_show_many_hours;
1692         }
1693 }
1694
1695 void
1696 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1697 {
1698         framepos_t pos;
1699         framepos_t spacer;
1700         long hrs, mins, secs, millisecs;
1701         gchar buf[16];
1702         gint n;
1703         ArdourCanvas::Ruler::Mark mark;
1704
1705         if (_session == 0) {
1706                 return;
1707         }
1708
1709         /* to prevent 'flashing' */
1710         if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1711                 lower = lower - spacer;
1712         } else {
1713                 lower = 0;
1714         }
1715
1716         pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1717
1718         switch (minsec_ruler_scale) {
1719
1720         case minsec_show_msecs:
1721                 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1722                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1723                         if (millisecs % minsec_mark_modulo == 0) {
1724                                 if (millisecs == 0) {
1725                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1726                                 } else {
1727                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1728                                 }
1729                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1730                         } else {
1731                                 buf[0] = '\0';
1732                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1733                         }
1734                         mark.label = buf;
1735                         mark.position = pos/1000.0;
1736                         marks.push_back (mark);
1737                 }
1738                 break;
1739
1740         case minsec_show_seconds:
1741                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1742                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1743                         if (secs % minsec_mark_modulo == 0) {
1744                                 if (secs == 0) {
1745                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1746                                 } else {
1747                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1748                                 }
1749                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1750                         } else {
1751                                 buf[0] = '\0';
1752                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1753                         }
1754                         mark.label = buf;
1755                         mark.position = pos/1000.0;
1756                         marks.push_back (mark);
1757                 }
1758                 break;
1759
1760         case minsec_show_minutes:
1761                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1762                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1763                         if (mins % minsec_mark_modulo == 0) {
1764                                 if (mins == 0) {
1765                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1766                                 } else {
1767                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1768                                 }
1769                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1770                         } else {
1771                                 buf[0] = '\0';
1772                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1773                         }
1774                         mark.label = buf;
1775                         mark.position = pos/1000.0;
1776                         marks.push_back (mark);
1777                 }
1778                 break;
1779
1780         case minsec_show_hours:
1781                  for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1782                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1783                         if (hrs % minsec_mark_modulo == 0) {
1784                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1785                                 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1786                         } else {
1787                                 buf[0] = '\0';
1788                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1789                         }
1790                         mark.label = buf;
1791                         mark.position = pos/1000.0;
1792                         marks.push_back (mark);
1793                  }
1794                  break;
1795
1796         case minsec_show_many_hours:
1797                 for (n = 0; n < minsec_nmarks; ) {
1798                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1799                         if (hrs % minsec_mark_modulo == 0) {
1800                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1801                                 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1802                                 mark.label = buf;
1803                                 mark.position = pos/1000.0;
1804                                 marks.push_back (mark);
1805                                 ++n;
1806                         }
1807                         pos += minsec_mark_interval;
1808                 }
1809                 break;
1810         }
1811 }