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