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