Use XMLNode::get/set_property API in EditorRulers class
[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                                 helper_active = true;
1158                         } else {
1159
1160                                 if ((*i).is_bar()) {
1161                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1162                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1163                                 } else if (((*i).beat % 2 == 1)) {
1164                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1165                                         buf[0] = '\0';
1166                                 } else {
1167                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1168                                         buf[0] = '\0';
1169                                 }
1170                                 mark.label = buf;
1171                                 mark.position = (*i).frame;
1172                                 marks.push_back (mark);
1173                                 n++;
1174                         }
1175                 }
1176                 break;
1177
1178         case bbt_show_ticks:
1179
1180                 beats = distance (grid.begin(), grid.end());
1181                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1182
1183                 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1184
1185                 // could do marks.assign() here to preallocate
1186
1187                 mark.label = "";
1188                 mark.position = lower;
1189                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1190                 marks.push_back (mark);
1191
1192                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1193
1194                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1195                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1196                                 edit_last_mark_label (marks, buf);
1197                                 helper_active = true;
1198                         } else {
1199
1200                                 if ((*i).is_bar()) {
1201                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1202                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1203                                 } else {
1204                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1205                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1206                                 }
1207                                 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1208                                         buf[0] = '\0';
1209                                 }
1210                                 mark.label =  buf;
1211                                 mark.position = (*i).frame;
1212                                 marks.push_back (mark);
1213                                 n++;
1214                         }
1215
1216                         /* Add the tick marks */
1217                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1218                         tick = skip; // the first non-beat tick
1219                         t = 0;
1220                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1221
1222                                 next_beat.beats = (*i).beat;
1223                                 next_beat.bars = (*i).bar;
1224                                 next_beat.ticks = tick;
1225                                 pos = _session->tempo_map().frame_at_bbt (next_beat);
1226
1227                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1228                                         i_am_accented = true;
1229                                 }
1230                                 mark.label = "";
1231                                 mark.position = pos;
1232
1233                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1234                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1235                                 } else {
1236                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1237                                 }
1238                                 i_am_accented = false;
1239                                 marks.push_back (mark);
1240
1241                                 tick += skip;
1242                                 ++t;
1243                                 ++n;
1244                         }
1245                 }
1246
1247           break;
1248
1249         case bbt_show_ticks_detail:
1250
1251                 beats = distance (grid.begin(), grid.end());
1252                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1253
1254                 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1255
1256                 mark.label = "";
1257                 mark.position = lower;
1258                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1259                 marks.push_back (mark);
1260
1261                 for (n = 1,   i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1262
1263                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1264                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1265                                 edit_last_mark_label (marks, buf);
1266                                 helper_active = true;
1267                         } else {
1268
1269                                 if ((*i).is_bar()) {
1270                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1271                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1272                                 } else {
1273                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1274                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1275                                 }
1276                                 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1277                                         buf[0] = '\0';
1278                                 }
1279                                 mark.label =  buf;
1280                                 mark.position = (*i).frame;
1281                                 marks.push_back (mark);
1282                                 n++;
1283                         }
1284
1285                         /* Add the tick marks */
1286                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1287                         tick = skip; // the first non-beat tick
1288
1289                         t = 0;
1290                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1291
1292                                 next_beat.beats = (*i).beat;
1293                                 next_beat.bars = (*i).bar;
1294                                 next_beat.ticks = tick;
1295                                 pos = _session->tempo_map().frame_at_bbt (next_beat);
1296
1297                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1298                                         i_am_accented = true;
1299                                 }
1300                                 if (i_am_accented && (pos > bbt_position_of_helper)){
1301                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1302                                 } else {
1303                                         buf[0] = '\0';
1304                                 }
1305
1306                                 mark.label = buf;
1307                                 mark.position = pos;
1308
1309                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1310                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1311                                 } else {
1312                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1313                                 }
1314                                 i_am_accented = false;
1315                                 marks.push_back (mark);
1316
1317                                 tick += skip;
1318                                 ++t;
1319                                 ++n;
1320                         }
1321                 }
1322
1323           break;
1324
1325         case bbt_show_ticks_super_detail:
1326
1327                 beats = distance (grid.begin(), grid.end());
1328                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1329
1330                 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1331
1332                 mark.label = "";
1333                 mark.position = lower;
1334                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1335                 marks.push_back (mark);
1336
1337                 for (n = 1,   i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1338
1339                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1340                                   snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1341                                   edit_last_mark_label (marks, buf);
1342                                   helper_active = true;
1343                         } else {
1344
1345                                   if ((*i).is_bar()) {
1346                                           mark.style = ArdourCanvas::Ruler::Mark::Major;
1347                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1348                                   } else {
1349                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1350                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1351                                   }
1352                                   if (((*i).frame < bbt_position_of_helper) && helper_active) {
1353                                           buf[0] = '\0';
1354                                   }
1355                                   mark.label =  buf;
1356                                   mark.position = (*i).frame;
1357                                   marks.push_back (mark);
1358                                   n++;
1359                         }
1360
1361                         /* Add the tick marks */
1362                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1363
1364                         next_beat.beats = (*i).beat;
1365                         next_beat.bars = (*i).bar;
1366                         tick = skip; // the first non-beat tick
1367                         t = 0;
1368                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1369
1370                                 next_beat.ticks = tick;
1371                                 pos = _session->tempo_map().frame_at_bbt (next_beat);
1372                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1373                                         i_am_accented = true;
1374                                 }
1375
1376                                 if (pos > bbt_position_of_helper) {
1377                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1378                                 } else {
1379                                         buf[0] = '\0';
1380                                 }
1381
1382                                 mark.label = buf;
1383                                 mark.position = pos;
1384
1385                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1386                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1387                                 } else {
1388                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1389                                 }
1390                                 i_am_accented = false;
1391                                 marks.push_back (mark);
1392
1393                                 tick += skip;
1394                                 ++t;
1395                                 ++n;
1396                         }
1397                 }
1398
1399           break;
1400
1401         case bbt_show_many:
1402                 bbt_nmarks = 1;
1403                 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1404                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1405                 mark.label = buf;
1406                 mark.position = lower;
1407                 marks.push_back (mark);
1408                 break;
1409
1410         case bbt_show_64:
1411                         bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1412                         for (n = 0,   i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1413                                 if ((*i).is_bar()) {
1414                                         if ((*i).bar % 64 == 1) {
1415                                                 if ((*i).bar % 256 == 1) {
1416                                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1417                                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1418                                                 } else {
1419                                                         buf[0] = '\0';
1420                                                         if ((*i).bar % 256 == 129)  {
1421                                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1422                                                         } else {
1423                                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1424                                                         }
1425                                                 }
1426                                                 mark.label = buf;
1427                                                 mark.position = (*i).frame;
1428                                                 marks.push_back (mark);
1429                                                 ++n;
1430                                         }
1431                                 }
1432                         }
1433                         break;
1434
1435         case bbt_show_16:
1436                 bbt_nmarks = (bbt_bars / 16) + 1;
1437                 for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1438                         if ((*i).is_bar()) {
1439                           if ((*i).bar % 16 == 1) {
1440                                 if ((*i).bar % 64 == 1) {
1441                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1442                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1443                                 } else {
1444                                         buf[0] = '\0';
1445                                         if ((*i).bar % 64 == 33)  {
1446                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1447                                         } else {
1448                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1449                                         }
1450                                 }
1451                                 mark.label = buf;
1452                                 mark.position = (*i).frame;
1453                                 marks.push_back (mark);
1454                                 ++n;
1455                           }
1456                         }
1457                 }
1458           break;
1459
1460         case bbt_show_4:
1461                 bbt_nmarks = (bbt_bars / 4) + 1;
1462                 for (n = 0,   i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1463                         if ((*i).is_bar()) {
1464                           if ((*i).bar % 4 == 1) {
1465                                 if ((*i).bar % 16 == 1) {
1466                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1467                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1468                                 } else {
1469                                         buf[0] = '\0';
1470                                         if ((*i).bar % 16 == 9)  {
1471                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1472                                         } else {
1473                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1474                                         }
1475                                 }
1476                                 mark.label = buf;
1477                                 mark.position = (*i).frame;
1478                                 marks.push_back (mark);
1479                                 ++n;
1480                           }
1481                         }
1482                 }
1483           break;
1484
1485         case bbt_show_1:
1486 //      default:
1487                 bbt_nmarks = bbt_bars + 2;
1488                 for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1489                         if ((*i).is_bar()) {
1490                           if ((*i).bar % 4 == 1) {
1491                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1492                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1493                           } else {
1494                                   buf[0] = '\0';
1495                                   if ((*i).bar % 4 == 3)  {
1496                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1497                                   } else {
1498                                           mark.style = ArdourCanvas::Ruler::Mark::Micro;
1499                                   }
1500                           }
1501                           mark.label = buf;
1502                           mark.position = (*i).frame;
1503                           marks.push_back (mark);
1504                           ++n;
1505                         }
1506                 }
1507                 break;
1508
1509         }
1510 }
1511
1512 void
1513 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1514 {
1515         _samples_ruler_interval = (upper - lower) / 5;
1516 }
1517
1518 void
1519 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1520 {
1521         framepos_t pos;
1522         framepos_t const ilower = (framepos_t) floor (lower);
1523         gchar buf[16];
1524         gint nmarks;
1525         gint n;
1526         ArdourCanvas::Ruler::Mark mark;
1527
1528         if (_session == 0) {
1529                 return;
1530         }
1531
1532         nmarks = 5;
1533         for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1534                 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1535                 mark.label = buf;
1536                 mark.position = pos;
1537                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1538                 marks.push_back (mark);
1539         }
1540 }
1541
1542 static void
1543 sample_to_clock_parts ( framepos_t sample,
1544                         framepos_t sample_rate,
1545                         long *hrs_p,
1546                         long *mins_p,
1547                         long *secs_p,
1548                         long *millisecs_p)
1549
1550 {
1551         framepos_t left;
1552         long hrs;
1553         long mins;
1554         long secs;
1555         long millisecs;
1556
1557         left = sample;
1558         hrs = left / (sample_rate * 60 * 60 * 1000);
1559         left -= hrs * sample_rate * 60 * 60 * 1000;
1560         mins = left / (sample_rate * 60 * 1000);
1561         left -= mins * sample_rate * 60 * 1000;
1562         secs = left / (sample_rate * 1000);
1563         left -= secs * sample_rate * 1000;
1564         millisecs = left / sample_rate;
1565
1566         *millisecs_p = millisecs;
1567         *secs_p = secs;
1568         *mins_p = mins;
1569         *hrs_p = hrs;
1570
1571         return;
1572 }
1573
1574 void
1575 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1576 {
1577         framepos_t fr = _session->frame_rate() * 1000;
1578         framepos_t spacer;
1579
1580         if (_session == 0) {
1581                 return;
1582         }
1583
1584
1585         /* to prevent 'flashing' */
1586         if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1587                 lower -= spacer;
1588         } else {
1589                 lower = 0;
1590         }
1591         upper += spacer;
1592         framecnt_t const range = (upper - lower) * 1000;
1593
1594         if (range <= (fr / 10)) { /* 0-0.1 second */
1595                 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1596                 minsec_ruler_scale = minsec_show_msecs;
1597                 minsec_mark_modulo = 10;
1598                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1599         } else if (range <= (fr / 2)) { /* 0-0.5 second */
1600                 minsec_mark_interval = fr / 100;  /* show 1/100 seconds */
1601                 minsec_ruler_scale = minsec_show_msecs;
1602                 minsec_mark_modulo = 100;
1603                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1604         } else if (range <= fr) { /* 0-1 second */
1605                 minsec_mark_interval = fr / 10;  /* show 1/10 seconds */
1606                 minsec_ruler_scale = minsec_show_msecs;
1607                 minsec_mark_modulo = 200;
1608                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1609         } else if (range <= 2 * fr) { /* 1-2 seconds */
1610                 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1611                 minsec_ruler_scale = minsec_show_msecs;
1612                 minsec_mark_modulo = 500;
1613                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1614         } else if (range <= 8 * fr) { /* 2-5 seconds */
1615                 minsec_mark_interval =  fr / 5; /* show 2 seconds */
1616                 minsec_ruler_scale = minsec_show_msecs;
1617                 minsec_mark_modulo = 1000;
1618                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1619         } else if (range <= 16 * fr) { /* 8-16 seconds */
1620                 minsec_mark_interval =  fr; /* show 1 seconds */
1621                 minsec_ruler_scale = minsec_show_seconds;
1622                 minsec_mark_modulo = 2;
1623                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1624         } else if (range <= 30 * fr) { /* 10-30 seconds */
1625                 minsec_mark_interval =  fr; /* show 1 seconds */
1626                 minsec_ruler_scale = minsec_show_seconds;
1627                 minsec_mark_modulo = 5;
1628                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1629         } else if (range <= 60 * fr) { /* 30-60 seconds */
1630                 minsec_mark_interval = fr; /* show 1 seconds */
1631                 minsec_ruler_scale = minsec_show_seconds;
1632                 minsec_mark_modulo = 5;
1633                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1634         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1635                 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1636                 minsec_ruler_scale = minsec_show_seconds;
1637                 minsec_mark_modulo = 3;
1638                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1639         } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1640                 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1641                 minsec_ruler_scale = minsec_show_seconds;
1642                 minsec_mark_modulo = 30;
1643                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1644         } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1645                 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1646                 minsec_ruler_scale = minsec_show_seconds;
1647                 minsec_mark_modulo = 120;
1648                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1649         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1650                 minsec_mark_interval =  60 * fr; /* show 1 minute */
1651                 minsec_ruler_scale = minsec_show_minutes;
1652                 minsec_mark_modulo = 5;
1653                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1654         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1655                 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1656                 minsec_ruler_scale = minsec_show_minutes;
1657                 minsec_mark_modulo = 10;
1658                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1659         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1660                 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1661                 minsec_ruler_scale = minsec_show_minutes;
1662                 minsec_mark_modulo = 30;
1663                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1664         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1665                 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1666                 minsec_ruler_scale = minsec_show_minutes;
1667                 minsec_mark_modulo = 60;
1668                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1669         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1670                 minsec_mark_interval =  60 * 60 * fr; /* show 60 minutes */
1671                 minsec_ruler_scale = minsec_show_hours;
1672                 minsec_mark_modulo = 2;
1673                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1674         } else {
1675
1676                 const framecnt_t hours_in_range = range / (60 * 60 * fr);
1677                 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1678
1679                 /* Normally we do not need to know anything about the width of the canvas
1680                    to set the ruler scale, because the caller has already determined
1681                    the width and set lower + upper arguments to this function to match that.
1682
1683                    But in this case, where the range defined by lower and uppper can vary
1684                    substantially (anything from 24hrs+ to several billion years)
1685                    trying to decide which tick marks to show does require us to know
1686                    about the available width.
1687                 */
1688
1689                 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1690                 minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1691                 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1692                 minsec_ruler_scale = minsec_show_many_hours;
1693         }
1694 }
1695
1696 void
1697 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1698 {
1699         framepos_t pos;
1700         framepos_t spacer;
1701         long hrs, mins, secs, millisecs;
1702         gchar buf[16];
1703         gint n;
1704         ArdourCanvas::Ruler::Mark mark;
1705
1706         if (_session == 0) {
1707                 return;
1708         }
1709
1710         /* to prevent 'flashing' */
1711         if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1712                 lower = lower - spacer;
1713         } else {
1714                 lower = 0;
1715         }
1716
1717         pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1718
1719         switch (minsec_ruler_scale) {
1720
1721         case minsec_show_msecs:
1722                 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1723                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1724                         if (millisecs % minsec_mark_modulo == 0) {
1725                                 if (millisecs == 0) {
1726                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1727                                 } else {
1728                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1729                                 }
1730                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1731                         } else {
1732                                 buf[0] = '\0';
1733                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1734                         }
1735                         mark.label = buf;
1736                         mark.position = pos/1000.0;
1737                         marks.push_back (mark);
1738                 }
1739                 break;
1740
1741         case minsec_show_seconds:
1742                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1743                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1744                         if (secs % minsec_mark_modulo == 0) {
1745                                 if (secs == 0) {
1746                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1747                                 } else {
1748                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1749                                 }
1750                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1751                         } else {
1752                                 buf[0] = '\0';
1753                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1754                         }
1755                         mark.label = buf;
1756                         mark.position = pos/1000.0;
1757                         marks.push_back (mark);
1758                 }
1759                 break;
1760
1761         case minsec_show_minutes:
1762                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1763                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1764                         if (mins % minsec_mark_modulo == 0) {
1765                                 if (mins == 0) {
1766                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1767                                 } else {
1768                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1769                                 }
1770                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1771                         } else {
1772                                 buf[0] = '\0';
1773                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1774                         }
1775                         mark.label = buf;
1776                         mark.position = pos/1000.0;
1777                         marks.push_back (mark);
1778                 }
1779                 break;
1780
1781         case minsec_show_hours:
1782                  for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1783                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1784                         if (hrs % minsec_mark_modulo == 0) {
1785                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1786                                 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1787                         } else {
1788                                 buf[0] = '\0';
1789                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1790                         }
1791                         mark.label = buf;
1792                         mark.position = pos/1000.0;
1793                         marks.push_back (mark);
1794                  }
1795                  break;
1796
1797         case minsec_show_many_hours:
1798                 for (n = 0; n < minsec_nmarks; ) {
1799                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1800                         if (hrs % minsec_mark_modulo == 0) {
1801                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1802                                 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1803                                 mark.label = buf;
1804                                 mark.position = pos/1000.0;
1805                                 marks.push_back (mark);
1806                                 ++n;
1807                         }
1808                         pos += minsec_mark_interval;
1809                 }
1810                 break;
1811         }
1812 }