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