new_grid: fix some thinkos in ruler math, to better match sensible grid lines.
[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 (samplepos_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_grid (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         samplepos_t rightmost_sample = _leftmost_sample + current_page_samples();
606
607         if (ruler_timecode_action->get_active()) {
608                 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
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_sample, _leftmost_sample + current_page_samples());
621         }
622
623         if (ruler_minsec_action->get_active()) {
624                 set_minsec_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
625         }
626
627         if (ruler_samples_action->get_active()) {
628                 set_samples_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
629         }
630 }
631
632 void
633 Editor::update_fixed_rulers ()
634 {
635         samplepos_t rightmost_sample;
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_sample = _leftmost_sample + 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_sample, rightmost_sample);
655         }
656
657         if (ruler_samples_action->get_active()) {
658                 samples_ruler->set_range (_leftmost_sample, rightmost_sample);
659         }
660
661         if (ruler_minsec_action->get_active()) {
662                 minsec_ruler->set_range (_leftmost_sample, rightmost_sample);
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_sample, _leftmost_sample+current_page_samples());
677         }
678 }
679
680
681 void
682 Editor::set_timecode_ruler_scale (samplepos_t lower, samplepos_t upper)
683 {
684         using namespace std;
685
686         samplepos_t spacer;
687         samplepos_t fr;
688
689         if (_session == 0) {
690                 return;
691         }
692
693         fr = _session->sample_rate();
694
695         if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
696                 lower = lower - spacer;
697         } else {
698                 lower = 0;
699         }
700
701         upper = upper + spacer;
702         samplecnt_t const range = upper - lower;
703
704         if (range < (2 * _session->samples_per_timecode_frame())) { /* 0 - 2 samples */
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 samples - 0.250 second */
709                 timecode_ruler_scale = timecode_show_samples;
710                 timecode_mark_modulo = 1;
711                 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
712         } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
713                 timecode_ruler_scale = timecode_show_samples;
714                 timecode_mark_modulo = 2;
715                 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
716         } else if (range <= fr) { /* 0.5-1 second */
717                 timecode_ruler_scale = timecode_show_samples;
718                 timecode_mark_modulo = 5;
719                 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
720         } else if (range <= 2 * fr) { /* 1-2 seconds */
721                 timecode_ruler_scale = timecode_show_samples;
722                 timecode_mark_modulo = 10;
723                 timecode_nmarks = 2 + (range / (samplepos_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 samplecnt_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 ((samplecnt_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         samplepos_t pos;
797         samplecnt_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 = (samplecnt_t)(128 * Editor::get_current_zoom ()))) {
808                 lower = lower - spacer;
809         } else {
810                 lower = 0;
811         }
812
813         pos = (samplecnt_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_samples:
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 sample down
845                 Timecode::frames_floot (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 (samplepos_t lower, samplepos_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_sample (lower)));
973
974         if (floor_lower_beat < 0.0) {
975                 floor_lower_beat = 0.0;
976         }
977
978         const samplecnt_t beat_before_lower_pos = _session->tempo_map().sample_at_beat (floor_lower_beat);
979         const samplecnt_t beat_after_upper_pos = _session->tempo_map().sample_at_beat (floor (max (0.0, _session->tempo_map().beat_at_sample (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 (_grid_type) {
993         case GridTypeBeatDiv2:
994                 bbt_beat_subdivision = 2;
995                 break;
996         case GridTypeBeatDiv3:
997                 bbt_beat_subdivision = 3;
998                 break;
999         case GridTypeBeatDiv4:
1000                 bbt_beat_subdivision = 4;
1001                 break;
1002         case GridTypeBeatDiv5:
1003                 bbt_beat_subdivision = 5;
1004                 bbt_accent_modulo = 2; // XXX YIKES
1005                 break;
1006         case GridTypeBeatDiv6:
1007                 bbt_beat_subdivision = 3;
1008                 bbt_accent_modulo = 2; // XXX YIKES
1009                 break;
1010         case GridTypeBeatDiv7:
1011                 bbt_beat_subdivision = 7;
1012                 bbt_accent_modulo = 2; // XXX YIKES
1013                 break;
1014         case GridTypeBeatDiv8:
1015                 bbt_beat_subdivision = 4;
1016                 bbt_accent_modulo = 2;
1017                 break;
1018         case GridTypeBeatDiv10:
1019                 bbt_beat_subdivision = 5;
1020                 bbt_accent_modulo = 2; // XXX YIKES
1021                 break;
1022         case GridTypeBeatDiv12:
1023                 bbt_beat_subdivision = 3;
1024                 bbt_accent_modulo = 3;
1025                 break;
1026         case GridTypeBeatDiv14:
1027                 bbt_beat_subdivision = 7;
1028                 bbt_accent_modulo = 3; // XXX YIKES!
1029                 break;
1030         case GridTypeBeatDiv16:
1031                 bbt_beat_subdivision = 4;
1032                 bbt_accent_modulo = 4;
1033                 break;
1034         case GridTypeBeatDiv20:
1035                 bbt_beat_subdivision = 5;
1036                 bbt_accent_modulo = 5;
1037                 break;
1038         case GridTypeBeatDiv24:
1039                 bbt_beat_subdivision = 6;
1040                 bbt_accent_modulo = 6;
1041                 break;
1042         case GridTypeBeatDiv28:
1043                 bbt_beat_subdivision = 7;
1044                 bbt_accent_modulo = 7;
1045                 break;
1046         case GridTypeBeatDiv32:
1047                 bbt_beat_subdivision = 4;
1048                 bbt_accent_modulo = 8;
1049                 break;
1050         case GridTypeBar:
1051         case GridTypeBeat:
1052                 bbt_beat_subdivision = 4;
1053                 break;
1054         case GridTypeNone:
1055         case GridTypeTimecode:
1056         case GridTypeMinSec:
1057         case GridTypeCDFrame:
1058                 bbt_beat_subdivision = 4;
1059                 break;
1060         }
1061
1062         const double ceil_upper_beat = floor (max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0;
1063         if (ceil_upper_beat == floor_lower_beat) {
1064                 return;
1065         }
1066
1067         bbt_bars = _session->tempo_map().bbt_at_beat (ceil_upper_beat).bars - _session->tempo_map().bbt_at_beat (floor_lower_beat).bars;
1068
1069         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
1070         double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / 5.0;
1071
1072         /* Only show the bar helper if there aren't many bars on the screen */
1073         if ((bbt_bars < 2) || (beats < 5)) {
1074                 bbt_bar_helper_on = true;
1075         }
1076
1077         //set upper limits on the beat_density based on the user's grid selection
1078         if (_grid_type == GridTypeBar) {
1079                 beat_density = fmax (beat_density, 16.01);
1080         } else if (_grid_type == GridTypeBeat) {
1081                 beat_density = fmax (beat_density, 4.01);
1082         }  else if (_grid_type == GridTypeBeatDiv2) {
1083                 beat_density = fmax (beat_density, 2.01);
1084         }  else if (_grid_type == GridTypeBeatDiv4) {
1085                 beat_density = fmax (beat_density, 1.001);
1086         } else if (_grid_type == GridTypeBeatDiv8) {
1087                 beat_density = fmax (beat_density, 0.501);
1088         } else if (_grid_type == GridTypeBeatDiv16) {
1089                 beat_density = fmax (beat_density, 0.2501);
1090         } else if (_grid_type == GridTypeBeatDiv32) {
1091                 beat_density = fmax (beat_density, 0.12501);
1092         }
1093
1094         if (beat_density > 2048) {
1095                 bbt_ruler_scale = bbt_show_many;
1096         } else if (beat_density > 512) {
1097                 bbt_ruler_scale = bbt_show_64;
1098         } else if (beat_density > 256) {
1099                 bbt_ruler_scale = bbt_show_16;
1100         } else if (beat_density > 64) {
1101                 bbt_ruler_scale = bbt_show_4;
1102         } else if (beat_density > 16) {
1103                 bbt_ruler_scale = bbt_show_1;
1104         } else if (beat_density > 4) {
1105                 bbt_ruler_scale =  bbt_show_quarters;
1106         } else  if (beat_density > 2) {
1107                 bbt_ruler_scale =  bbt_show_eighths;
1108         } else  if (beat_density > 1) {
1109                 bbt_ruler_scale =  bbt_show_sixteenths;
1110         } else {
1111                 bbt_ruler_scale =  bbt_show_thirtyseconds;
1112         }
1113 }
1114
1115 static void
1116 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1117 {
1118         ArdourCanvas::Ruler::Mark copy = marks.back();
1119         copy.label = newlabel;
1120         marks.pop_back ();
1121         marks.push_back (copy);
1122 }
1123
1124 void
1125 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1126 {
1127         if (_session == 0) {
1128                 return;
1129         }
1130
1131         std::vector<TempoMap::BBTPoint>::const_iterator i;
1132
1133         char buf[64];
1134         gint  n = 0;
1135         samplepos_t pos;
1136         Timecode::BBT_Time next_beat;
1137         uint32_t beats = 0;
1138         uint32_t tick = 0;
1139         uint32_t skip;
1140         uint32_t t;
1141         double bbt_position_of_helper;
1142         bool i_am_accented = false;
1143         bool helper_active = false;
1144         ArdourCanvas::Ruler::Mark mark;
1145
1146         std::vector<TempoMap::BBTPoint> grid;
1147
1148         compute_current_bbt_points (grid, lower, upper);
1149
1150         if (distance (grid.begin(), grid.end()) == 0) {
1151                 return;
1152         }
1153
1154         switch (bbt_ruler_scale) {
1155
1156         case bbt_show_quarters:
1157
1158                 beats = distance (grid.begin(), grid.end());
1159                 bbt_nmarks = beats + 2;
1160
1161                 mark.label = "";
1162                 mark.position = lower;
1163                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1164                 marks.push_back (mark);
1165
1166                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1167
1168                         if ((*i).sample < lower && (bbt_bar_helper_on)) {
1169                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1170                                 edit_last_mark_label (marks, buf);
1171                         } else {
1172
1173                                 if ((*i).is_bar()) {
1174                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1175                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1176                                 } else if (((*i).beat % 2 == 1)) {
1177                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1178                                         buf[0] = '\0';
1179                                 } else {
1180                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1181                                         buf[0] = '\0';
1182                                 }
1183                                 mark.label = buf;
1184                                 mark.position = (*i).sample;
1185                                 marks.push_back (mark);
1186                                 n++;
1187                         }
1188                 }
1189                 break;
1190
1191         case bbt_show_eighths:
1192
1193                 beats = distance (grid.begin(), grid.end());
1194                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1195
1196                 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1197
1198                 // could do marks.assign() here to preallocate
1199
1200                 mark.label = "";
1201                 mark.position = lower;
1202                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1203                 marks.push_back (mark);
1204
1205                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1206
1207                         if ((*i).sample < lower && (bbt_bar_helper_on)) {
1208                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1209                                 edit_last_mark_label (marks, buf);
1210                                 helper_active = true;
1211                         } else {
1212
1213                                 if ((*i).is_bar()) {
1214                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1215                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1216                                 } else {
1217                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1218                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1219                                 }
1220                                 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1221                                         buf[0] = '\0';
1222                                 }
1223                                 mark.label =  buf;
1224                                 mark.position = (*i).sample;
1225                                 marks.push_back (mark);
1226                                 n++;
1227                         }
1228
1229                         /* Add the tick marks */
1230                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1231                         tick = skip; // the first non-beat tick
1232                         t = 0;
1233                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1234
1235                                 next_beat.beats = (*i).beat;
1236                                 next_beat.bars = (*i).bar;
1237                                 next_beat.ticks = tick;
1238                                 pos = _session->tempo_map().sample_at_bbt (next_beat);
1239
1240                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1241                                         i_am_accented = true;
1242                                 }
1243                                 mark.label = "";
1244                                 mark.position = pos;
1245
1246                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1247                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1248                                 } else {
1249                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1250                                 }
1251                                 i_am_accented = false;
1252                                 marks.push_back (mark);
1253
1254                                 tick += skip;
1255                                 ++t;
1256                                 ++n;
1257                         }
1258                 }
1259
1260           break;
1261
1262         case bbt_show_sixteenths:
1263
1264                 beats = distance (grid.begin(), grid.end());
1265                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1266
1267                 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1268
1269                 mark.label = "";
1270                 mark.position = lower;
1271                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1272                 marks.push_back (mark);
1273
1274                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1275
1276                         if ((*i).sample < lower && (bbt_bar_helper_on)) {
1277                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1278                                 edit_last_mark_label (marks, buf);
1279                                 helper_active = true;
1280                         } else {
1281
1282                                 if ((*i).is_bar()) {
1283                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1284                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1285                                 } else {
1286                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1287                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1288                                 }
1289                                 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1290                                         buf[0] = '\0';
1291                                 }
1292                                 mark.label =  buf;
1293                                 mark.position = (*i).sample;
1294                                 marks.push_back (mark);
1295                                 n++;
1296                         }
1297
1298                         /* Add the tick marks */
1299                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1300                         tick = skip; // the first non-beat tick
1301
1302                         t = 0;
1303                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1304
1305                                 next_beat.beats = (*i).beat;
1306                                 next_beat.bars = (*i).bar;
1307                                 next_beat.ticks = tick;
1308                                 pos = _session->tempo_map().sample_at_bbt (next_beat);
1309
1310                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1311                                         i_am_accented = true;
1312                                 }
1313                                 if (i_am_accented && (pos > bbt_position_of_helper)){
1314                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1315                                 } else {
1316                                         buf[0] = '\0';
1317                                 }
1318
1319                                 mark.label = buf;
1320                                 mark.position = pos;
1321
1322                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1323                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1324                                 } else {
1325                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1326                                 }
1327                                 i_am_accented = false;
1328                                 marks.push_back (mark);
1329
1330                                 tick += skip;
1331                                 ++t;
1332                                 ++n;
1333                         }
1334                 }
1335
1336           break;
1337
1338         case bbt_show_thirtyseconds:
1339
1340                 beats = distance (grid.begin(), grid.end());
1341                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1342
1343                 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1344
1345                 mark.label = "";
1346                 mark.position = lower;
1347                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1348                 marks.push_back (mark);
1349
1350                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1351
1352                         if ((*i).sample < lower && (bbt_bar_helper_on)) {
1353                                   snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1354                                   edit_last_mark_label (marks, buf);
1355                                   helper_active = true;
1356                         } else {
1357
1358                                   if ((*i).is_bar()) {
1359                                           mark.style = ArdourCanvas::Ruler::Mark::Major;
1360                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1361                                   } else {
1362                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1363                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1364                                   }
1365                                   if (((*i).sample < bbt_position_of_helper) && helper_active) {
1366                                           buf[0] = '\0';
1367                                   }
1368                                   mark.label =  buf;
1369                                   mark.position = (*i).sample;
1370                                   marks.push_back (mark);
1371                                   n++;
1372                         }
1373
1374                         /* Add the tick marks */
1375                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1376
1377                         next_beat.beats = (*i).beat;
1378                         next_beat.bars = (*i).bar;
1379                         tick = skip; // the first non-beat tick
1380                         t = 0;
1381                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1382
1383                                 next_beat.ticks = tick;
1384                                 pos = _session->tempo_map().sample_at_bbt (next_beat);
1385                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1386                                         i_am_accented = true;
1387                                 }
1388
1389                                 if (pos > bbt_position_of_helper) {
1390                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1391                                 } else {
1392                                         buf[0] = '\0';
1393                                 }
1394
1395                                 mark.label = buf;
1396                                 mark.position = pos;
1397
1398                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1399                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1400                                 } else {
1401                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1402                                 }
1403                                 i_am_accented = false;
1404                                 marks.push_back (mark);
1405
1406                                 tick += skip;
1407                                 ++t;
1408                                 ++n;
1409                         }
1410                 }
1411
1412           break;
1413
1414         case bbt_show_many:
1415                 bbt_nmarks = 1;
1416                 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars);
1417                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1418                 mark.label = buf;
1419                 mark.position = lower;
1420                 marks.push_back (mark);
1421                 break;
1422
1423         case bbt_show_64:
1424                         bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1425                         for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1426                                 if ((*i).is_bar()) {
1427                                         if ((*i).bar % 64 == 1) {
1428                                                 if ((*i).bar % 256 == 1) {
1429                                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1430                                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1431                                                 } else {
1432                                                         buf[0] = '\0';
1433                                                         if ((*i).bar % 256 == 129)  {
1434                                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1435                                                         } else {
1436                                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1437                                                         }
1438                                                 }
1439                                                 mark.label = buf;
1440                                                 mark.position = (*i).sample;
1441                                                 marks.push_back (mark);
1442                                                 ++n;
1443                                         }
1444                                 }
1445                         }
1446                         break;
1447
1448         case bbt_show_16:
1449                 bbt_nmarks = (bbt_bars / 16) + 1;
1450                 for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1451                         if ((*i).is_bar()) {
1452                           if ((*i).bar % 16 == 1) {
1453                                 if ((*i).bar % 64 == 1) {
1454                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1455                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1456                                 } else {
1457                                         buf[0] = '\0';
1458                                         if ((*i).bar % 64 == 33)  {
1459                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1460                                         } else {
1461                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1462                                         }
1463                                 }
1464                                 mark.label = buf;
1465                                 mark.position = (*i).sample;
1466                                 marks.push_back (mark);
1467                                 ++n;
1468                           }
1469                         }
1470                 }
1471           break;
1472
1473         case bbt_show_4:
1474                 bbt_nmarks = (bbt_bars / 4) + 1;
1475                 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1476                         if ((*i).is_bar()) {
1477                                 if ((*i).bar % 4 == 1) {
1478                                         if ((*i).bar % 16 == 1) {
1479                                                 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1480                                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1481                                         } else {
1482                                                 buf[0] = '\0';
1483                                                 if ((*i).bar % 16 == 9)  {
1484                                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1485                                                 } else {
1486                                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1487                                                 }
1488                                         }
1489                                         mark.label = buf;
1490                                         mark.position = (*i).sample;
1491                                         marks.push_back (mark);
1492                                         ++n;
1493                                 }
1494                         }
1495                 }
1496           break;
1497
1498         case bbt_show_1:
1499 //      default:
1500                 bbt_nmarks = bbt_bars + 2;
1501                 for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1502                         if ((*i).is_bar()) {
1503                           if ((*i).bar % 4 == 1) {
1504                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1505                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1506                           } else {
1507                                   buf[0] = '\0';
1508                                   if ((*i).bar % 4 == 3)  {
1509                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1510                                   } else {
1511                                           mark.style = ArdourCanvas::Ruler::Mark::Micro;
1512                                   }
1513                           }
1514                           mark.label = buf;
1515                           mark.position = (*i).sample;
1516                           marks.push_back (mark);
1517                           ++n;
1518                         }
1519                 }
1520                 break;
1521
1522         }
1523 }
1524
1525 void
1526 Editor::set_samples_ruler_scale (samplepos_t lower, samplepos_t upper)
1527 {
1528         _samples_ruler_interval = (upper - lower) / 5;
1529 }
1530
1531 void
1532 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1533 {
1534         samplepos_t pos;
1535         samplepos_t const ilower = (samplepos_t) floor (lower);
1536         gchar buf[16];
1537         gint nmarks;
1538         gint n;
1539         ArdourCanvas::Ruler::Mark mark;
1540
1541         if (_session == 0) {
1542                 return;
1543         }
1544
1545         nmarks = 5;
1546         for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1547                 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1548                 mark.label = buf;
1549                 mark.position = pos;
1550                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1551                 marks.push_back (mark);
1552         }
1553 }
1554
1555 static void
1556 sample_to_clock_parts (samplepos_t sample,
1557                        samplepos_t sample_rate,
1558                        long*       hrs_p,
1559                        long*       mins_p,
1560                        long*       secs_p,
1561                        long*       millisecs_p)
1562 {
1563         samplepos_t left;
1564         long hrs;
1565         long mins;
1566         long secs;
1567         long millisecs;
1568
1569         left = sample;
1570         hrs = left / (sample_rate * 60 * 60 * 1000);
1571         left -= hrs * sample_rate * 60 * 60 * 1000;
1572         mins = left / (sample_rate * 60 * 1000);
1573         left -= mins * sample_rate * 60 * 1000;
1574         secs = left / (sample_rate * 1000);
1575         left -= secs * sample_rate * 1000;
1576         millisecs = left / sample_rate;
1577
1578         *millisecs_p = millisecs;
1579         *secs_p = secs;
1580         *mins_p = mins;
1581         *hrs_p = hrs;
1582
1583         return;
1584 }
1585
1586 void
1587 Editor::set_minsec_ruler_scale (samplepos_t lower, samplepos_t upper)
1588 {
1589         samplepos_t fr = _session->sample_rate() * 1000;
1590         samplepos_t spacer;
1591
1592         if (_session == 0) {
1593                 return;
1594         }
1595
1596
1597         /* to prevent 'flashing' */
1598         if (lower > (spacer = (samplepos_t)(128 * Editor::get_current_zoom ()))) {
1599                 lower -= spacer;
1600         } else {
1601                 lower = 0;
1602         }
1603         upper += spacer;
1604         samplecnt_t const range = (upper - lower) * 1000;
1605
1606         if (range <= (fr / 10)) { /* 0-0.1 second */
1607                 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1608                 minsec_ruler_scale = minsec_show_msecs;
1609                 minsec_mark_modulo = 10;
1610                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1611         } else if (range <= (fr / 2)) { /* 0-0.5 second */
1612                 minsec_mark_interval = fr / 100;  /* show 1/100 seconds */
1613                 minsec_ruler_scale = minsec_show_msecs;
1614                 minsec_mark_modulo = 100;
1615                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1616         } else if (range <= fr) { /* 0-1 second */
1617                 minsec_mark_interval = fr / 10;  /* show 1/10 seconds */
1618                 minsec_ruler_scale = minsec_show_msecs;
1619                 minsec_mark_modulo = 200;
1620                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1621         } else if (range <= 2 * fr) { /* 1-2 seconds */
1622                 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1623                 minsec_ruler_scale = minsec_show_msecs;
1624                 minsec_mark_modulo = 500;
1625                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1626         } else if (range <= 8 * fr) { /* 2-5 seconds */
1627                 minsec_mark_interval =  fr / 5; /* show 2 seconds */
1628                 minsec_ruler_scale = minsec_show_msecs;
1629                 minsec_mark_modulo = 1000;
1630                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1631         } else if (range <= 16 * fr) { /* 8-16 seconds */
1632                 minsec_mark_interval =  fr; /* show 1 seconds */
1633                 minsec_ruler_scale = minsec_show_seconds;
1634                 minsec_mark_modulo = 2;
1635                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1636         } else if (range <= 30 * fr) { /* 10-30 seconds */
1637                 minsec_mark_interval =  fr; /* show 1 seconds */
1638                 minsec_ruler_scale = minsec_show_seconds;
1639                 minsec_mark_modulo = 5;
1640                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1641         } else if (range <= 60 * fr) { /* 30-60 seconds */
1642                 minsec_mark_interval = fr; /* show 1 seconds */
1643                 minsec_ruler_scale = minsec_show_seconds;
1644                 minsec_mark_modulo = 5;
1645                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1646         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1647                 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1648                 minsec_ruler_scale = minsec_show_seconds;
1649                 minsec_mark_modulo = 3;
1650                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1651         } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1652                 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1653                 minsec_ruler_scale = minsec_show_seconds;
1654                 minsec_mark_modulo = 30;
1655                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1656         } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1657                 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1658                 minsec_ruler_scale = minsec_show_seconds;
1659                 minsec_mark_modulo = 120;
1660                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1661         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1662                 minsec_mark_interval =  60 * fr; /* show 1 minute */
1663                 minsec_ruler_scale = minsec_show_minutes;
1664                 minsec_mark_modulo = 5;
1665                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1666         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1667                 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1668                 minsec_ruler_scale = minsec_show_minutes;
1669                 minsec_mark_modulo = 10;
1670                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1671         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1672                 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1673                 minsec_ruler_scale = minsec_show_minutes;
1674                 minsec_mark_modulo = 30;
1675                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1676         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1677                 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1678                 minsec_ruler_scale = minsec_show_minutes;
1679                 minsec_mark_modulo = 60;
1680                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1681         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1682                 minsec_mark_interval =  60 * 60 * fr; /* show 60 minutes */
1683                 minsec_ruler_scale = minsec_show_hours;
1684                 minsec_mark_modulo = 2;
1685                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1686         } else {
1687
1688                 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
1689                 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1690
1691                 /* Normally we do not need to know anything about the width of the canvas
1692                    to set the ruler scale, because the caller has already determined
1693                    the width and set lower + upper arguments to this function to match that.
1694
1695                    But in this case, where the range defined by lower and uppper can vary
1696                    substantially (anything from 24hrs+ to several billion years)
1697                    trying to decide which tick marks to show does require us to know
1698                    about the available width.
1699                 */
1700
1701                 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1702                 minsec_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1703                 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1704                 minsec_ruler_scale = minsec_show_many_hours;
1705         }
1706 }
1707
1708 void
1709 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1710 {
1711         samplepos_t pos;
1712         samplepos_t spacer;
1713         long hrs, mins, secs, millisecs;
1714         gchar buf[16];
1715         gint n;
1716         ArdourCanvas::Ruler::Mark mark;
1717
1718         if (_session == 0) {
1719                 return;
1720         }
1721
1722         /* to prevent 'flashing' */
1723         if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
1724                 lower = lower - spacer;
1725         } else {
1726                 lower = 0;
1727         }
1728
1729         if (minsec_mark_interval == 0) {  //we got here too early; divide-by-zero imminent
1730                 return;
1731         }
1732
1733         pos = (((1000 * (samplepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1734
1735         switch (minsec_ruler_scale) {
1736
1737         case minsec_show_msecs:
1738                 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1739                         sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1740                         if (millisecs % minsec_mark_modulo == 0) {
1741                                 if (millisecs == 0) {
1742                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1743                                 } else {
1744                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1745                                 }
1746                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1747                         } else {
1748                                 buf[0] = '\0';
1749                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1750                         }
1751                         mark.label = buf;
1752                         mark.position = pos/1000.0;
1753                         marks.push_back (mark);
1754                 }
1755                 break;
1756
1757         case minsec_show_seconds:
1758                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1759                         sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1760                         if (secs % minsec_mark_modulo == 0) {
1761                                 if (secs == 0) {
1762                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1763                                 } else {
1764                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1765                                 }
1766                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1767                         } else {
1768                                 buf[0] = '\0';
1769                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1770                         }
1771                         mark.label = buf;
1772                         mark.position = pos/1000.0;
1773                         marks.push_back (mark);
1774                 }
1775                 break;
1776
1777         case minsec_show_minutes:
1778                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1779                         sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1780                         if (mins % minsec_mark_modulo == 0) {
1781                                 if (mins == 0) {
1782                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1783                                 } else {
1784                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1785                                 }
1786                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
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_hours:
1798                  for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1799                         sample_to_clock_parts (pos, _session->sample_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:%02ld", hrs, mins);
1803                         } else {
1804                                 buf[0] = '\0';
1805                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1806                         }
1807                         mark.label = buf;
1808                         mark.position = pos/1000.0;
1809                         marks.push_back (mark);
1810                  }
1811                  break;
1812
1813         case minsec_show_many_hours:
1814                 for (n = 0; n < minsec_nmarks;) {
1815                         sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1816                         if (hrs % minsec_mark_modulo == 0) {
1817                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1818                                 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1819                                 mark.label = buf;
1820                                 mark.position = pos/1000.0;
1821                                 marks.push_back (mark);
1822                                 ++n;
1823                         }
1824                         pos += minsec_mark_interval;
1825                 }
1826                 break;
1827         }
1828 }