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