Merge branch 'cairocanvas'
[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_canvasvar_SmallerFont());
130 #else
131         Pango::FontDescription font (ARDOUR_UI::config()->get_canvasvar_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 void
718 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
719 {
720         framepos_t spacer;
721         framepos_t fr;
722
723         if (_session == 0) {
724                 return;
725         }
726
727         fr = _session->frame_rate();
728
729         if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
730                 lower = lower - spacer;
731         } else {
732                 lower = 0;
733         }
734         upper = upper + spacer;
735         framecnt_t const range = upper - lower;
736
737         if (range < (2 * _session->frames_per_timecode_frame())) { /* 0 - 2 frames */
738                 timecode_ruler_scale = timecode_show_bits;
739                 timecode_mark_modulo = 20;
740                 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
741         } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
742                 timecode_ruler_scale = timecode_show_frames;
743                 timecode_mark_modulo = 1;
744                 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
745         } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
746                 timecode_ruler_scale = timecode_show_frames;
747                 timecode_mark_modulo = 2;
748                 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
749         } else if (range <= fr) { /* 0.5-1 second */
750                 timecode_ruler_scale = timecode_show_frames;
751                 timecode_mark_modulo = 5;
752                 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
753         } else if (range <= 2 * fr) { /* 1-2 seconds */
754                 timecode_ruler_scale = timecode_show_frames;
755                 timecode_mark_modulo = 10;
756                 timecode_nmarks = 2 + (range / (framepos_t)_session->frames_per_timecode_frame());
757         } else if (range <= 8 * fr) { /* 2-8 seconds */
758                 timecode_ruler_scale = timecode_show_seconds;
759                 timecode_mark_modulo = 1;
760                 timecode_nmarks = 2 + (range / fr);
761         } else if (range <= 16 * fr) { /* 8-16 seconds */
762                 timecode_ruler_scale = timecode_show_seconds;
763                 timecode_mark_modulo = 2;
764                 timecode_nmarks = 2 + (range / fr);
765         } else if (range <= 30 * fr) { /* 16-30 seconds */
766                 timecode_ruler_scale = timecode_show_seconds;
767                 timecode_mark_modulo = 5;
768                 timecode_nmarks = 2 + (range / fr);
769         } else if (range <= 60 * fr) { /* 30-60 seconds */
770                 timecode_ruler_scale = timecode_show_seconds;
771                 timecode_mark_modulo = 5;
772                 timecode_nmarks = 2 + (range / fr);
773         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
774                 timecode_ruler_scale = timecode_show_seconds;
775                 timecode_mark_modulo = 15;
776                 timecode_nmarks = 2 + (range / fr);
777         } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
778                 timecode_ruler_scale = timecode_show_seconds;
779                 timecode_mark_modulo = 30;
780                 timecode_nmarks = 2 + (range / fr);
781         } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
782                 timecode_ruler_scale = timecode_show_minutes;
783                 timecode_mark_modulo = 2;
784                 timecode_nmarks = 2 + 10;
785         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
786                 timecode_ruler_scale = timecode_show_minutes;
787                 timecode_mark_modulo = 5;
788                 timecode_nmarks = 2 + 30;
789         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
790                 timecode_ruler_scale = timecode_show_minutes;
791                 timecode_mark_modulo = 10;
792                 timecode_nmarks = 2 + 60;
793         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
794                 timecode_ruler_scale = timecode_show_minutes;
795                 timecode_mark_modulo = 30;
796                 timecode_nmarks = 2 + (60 * 4);
797         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
798                 timecode_ruler_scale = timecode_show_hours;
799                 timecode_mark_modulo = 1;
800                 timecode_nmarks = 2 + 8;
801         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
802                 timecode_ruler_scale = timecode_show_hours;
803                 timecode_mark_modulo = 1;
804                 timecode_nmarks = 2 + 24;
805         } else {
806
807                 /* not possible if framepos_t is a 32 bit quantity */
808
809                 timecode_ruler_scale = timecode_show_hours;
810                 timecode_mark_modulo = 4;
811                 timecode_nmarks = 2 + 24;
812         }
813
814 }
815
816 void
817 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
818 {
819         framepos_t pos;
820         framecnt_t spacer;
821         Timecode::Time timecode;
822         gchar buf[16];
823         gint n;
824         ArdourCanvas::Ruler::Mark mark;
825
826         if (_session == 0) {
827                 return;
828         }
829
830         if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
831                 lower = lower - spacer;
832         } else {
833                 lower = 0;
834         }
835
836         pos = (framecnt_t) floor (lower);
837
838         switch (timecode_ruler_scale) {
839         case timecode_show_bits:
840
841                 // Find timecode time of this sample (pos) with subframe accuracy
842                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
843
844                 for (n = 0; n < timecode_nmarks; n++) {
845                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
846                         if ((timecode.subframes % timecode_mark_modulo) == 0) {
847                                 if (timecode.subframes == 0) {
848                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
849                                         snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
850                                 } else {
851                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
852                                         snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
853                                 }
854                         } else {
855                                 snprintf (buf, sizeof(buf)," ");
856                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
857                                 
858                         }
859                         mark.label = buf;
860                         mark.position = pos;
861
862                         marks.push_back (mark);
863
864                         // Increment subframes by one
865                         Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
866                 }
867           break;
868         case timecode_show_seconds:
869                 // Find timecode time of this sample (pos)
870                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
871                 // Go to next whole second down
872                 Timecode::seconds_floor( timecode );
873
874                 for (n = 0; n < timecode_nmarks; n++) {
875                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
876                         if ((timecode.seconds % timecode_mark_modulo) == 0) {
877                                 if (timecode.seconds == 0) {
878                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
879                                         mark.position = pos;
880                                 } else {
881                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
882                                         mark.position = pos;
883                                 }
884                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
885                         } else {
886                                 snprintf (buf, sizeof(buf)," ");
887                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
888                                 mark.position = pos;
889
890                         }
891                         mark.label = buf;
892                         marks.push_back (mark);
893
894                         Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
895                 }
896           break;
897         case timecode_show_minutes:
898                 // Find timecode time of this sample (pos)
899                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
900                 // Go to next whole minute down
901                 Timecode::minutes_floor( timecode );
902
903                 for (n = 0; n < timecode_nmarks; n++) {
904                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
905                         if ((timecode.minutes % timecode_mark_modulo) == 0) {
906                                 if (timecode.minutes == 0) {
907                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
908                                 } else {
909                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
910                                 }
911                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
912                         } else {
913                                 snprintf (buf, sizeof(buf)," ");
914                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
915
916                         }
917                         mark.label = buf;
918                         mark.position = pos;
919                         marks.push_back (mark);
920                         Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
921                 }
922
923           break;
924         case timecode_show_hours:
925                 // Find timecode time of this sample (pos)
926                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
927                 // Go to next whole hour down
928                 Timecode::hours_floor( timecode );
929
930                 for (n = 0; n < timecode_nmarks; n++) {
931                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
932                         if ((timecode.hours % timecode_mark_modulo) == 0) {
933                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
934                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
935                         } else {
936                                 snprintf (buf, sizeof(buf)," ");
937                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
938
939                         }
940                         mark.label = buf;
941                         mark.position = pos;
942                         marks.push_back (mark);
943                         Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
944                 }
945           break;
946         case timecode_show_frames:
947                 // Find timecode time of this sample (pos)
948                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
949                 // Go to next whole frame down
950                 Timecode::frames_floor( timecode );
951
952                 for (n = 0; n < timecode_nmarks; n++) {
953                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
954                         if ((timecode.frames % timecode_mark_modulo) == 0)  {
955                                 if (timecode.frames == 0) {
956                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
957                                 } else {
958                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
959                                 }
960                                 mark.position = pos;
961                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
962                         } else {
963                                 snprintf (buf, sizeof(buf)," ");
964                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
965                                 mark.position = pos;
966
967                         }
968                         mark.label = buf;
969                         marks.push_back (mark);
970                         Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
971                 }
972
973           break;
974         }
975 }
976
977
978
979 void
980 Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
981                                  ARDOUR::TempoMap::BBTPointList::const_iterator begin,
982                                  ARDOUR::TempoMap::BBTPointList::const_iterator end)
983 {
984         if (_session == 0) {
985                 return;
986         }
987
988         TempoMap::BBTPointList::const_iterator i;
989         Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
990
991         _session->bbt_time (lower, lower_beat);
992         _session->bbt_time (upper, upper_beat);
993         uint32_t beats = 0;
994
995         bbt_accent_modulo = 1;
996         bbt_bar_helper_on = false;
997         bbt_bars = 0;
998         bbt_nmarks = 1;
999
1000         bbt_ruler_scale =  bbt_over;
1001
1002         switch (_snap_type) {
1003         case SnapToBeatDiv2:
1004                 bbt_beat_subdivision = 2;
1005                 break;
1006         case SnapToBeatDiv3:
1007                 bbt_beat_subdivision = 3;
1008                 break;
1009         case SnapToBeatDiv4:
1010                 bbt_beat_subdivision = 4;
1011                 break;
1012         case SnapToBeatDiv5:
1013                 bbt_beat_subdivision = 5;
1014                 bbt_accent_modulo = 2; // XXX YIKES
1015                 break;
1016         case SnapToBeatDiv6:
1017                 bbt_beat_subdivision = 6;
1018                 bbt_accent_modulo = 2; // XXX YIKES
1019                 break;
1020         case SnapToBeatDiv7:
1021                 bbt_beat_subdivision = 7;
1022                 bbt_accent_modulo = 2; // XXX YIKES
1023                 break;
1024         case SnapToBeatDiv8:
1025                 bbt_beat_subdivision = 8;
1026                 bbt_accent_modulo = 2;
1027                 break;
1028         case SnapToBeatDiv10:
1029                 bbt_beat_subdivision = 10;
1030                 bbt_accent_modulo = 2; // XXX YIKES
1031                 break;
1032         case SnapToBeatDiv12:
1033                 bbt_beat_subdivision = 12;
1034                 bbt_accent_modulo = 3;
1035                 break;
1036         case SnapToBeatDiv14:
1037                 bbt_beat_subdivision = 14;
1038                 bbt_accent_modulo = 3; // XXX YIKES!
1039                 break;
1040         case SnapToBeatDiv16:
1041                 bbt_beat_subdivision = 16;
1042                 bbt_accent_modulo = 4;
1043                 break;
1044         case SnapToBeatDiv20:
1045                 bbt_beat_subdivision = 20;
1046                 bbt_accent_modulo = 5;
1047                 break;
1048         case SnapToBeatDiv24:
1049                 bbt_beat_subdivision = 24;
1050                 bbt_accent_modulo = 6;
1051                 break;
1052         case SnapToBeatDiv28:
1053                 bbt_beat_subdivision = 28;
1054                 bbt_accent_modulo = 7;
1055                 break;
1056         case SnapToBeatDiv32:
1057                 bbt_beat_subdivision = 32;
1058                 bbt_accent_modulo = 8;
1059                 break;
1060         case SnapToBeatDiv64:
1061                 bbt_beat_subdivision = 64;
1062                 bbt_accent_modulo = 8;
1063                 break;
1064         case SnapToBeatDiv128:
1065                 bbt_beat_subdivision = 128;
1066                 bbt_accent_modulo = 8;
1067                 break;
1068         default:
1069                 bbt_beat_subdivision = 4;
1070                 break;
1071         }
1072
1073         if (distance (begin, end) == 0) {
1074                 return;
1075         }
1076
1077         i = end;
1078         i--;
1079         if ((*i).beat >= (*begin).beat) {
1080                 bbt_bars = (*i).bar - (*begin).bar;
1081         } else {
1082                 bbt_bars = (*i).bar - (*begin).bar - 1;
1083         }
1084         beats = distance (begin, end) - bbt_bars;
1085
1086         /* Only show the bar helper if there aren't many bars on the screen */
1087         if ((bbt_bars < 2) || (beats < 5)) {
1088                 bbt_bar_helper_on = true;
1089         }
1090
1091         if (bbt_bars > 8192) {
1092                 bbt_ruler_scale =  bbt_over;
1093         } else if (bbt_bars > 1024) {
1094                 bbt_ruler_scale = bbt_show_64;
1095         } else if (bbt_bars > 256) {
1096                 bbt_ruler_scale = bbt_show_16;
1097         } else if (bbt_bars > 64) {
1098                 bbt_ruler_scale = bbt_show_4;
1099         } else if (bbt_bars > 10) {
1100                 bbt_ruler_scale =  bbt_show_1;
1101         } else if (bbt_bars > 2) {
1102                 bbt_ruler_scale =  bbt_show_beats;
1103         } else  if (bbt_bars > 0) {
1104                 bbt_ruler_scale =  bbt_show_ticks;
1105         } else {
1106                 bbt_ruler_scale =  bbt_show_ticks_detail;
1107         }
1108         
1109         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)) {
1110                 bbt_ruler_scale =  bbt_show_ticks_super_detail;
1111         }
1112 }
1113
1114 static void 
1115 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1116 {
1117         ArdourCanvas::Ruler::Mark copy = marks.back();
1118         copy.label = newlabel;
1119         marks.pop_back ();
1120         marks.push_back (copy);
1121 }               
1122
1123 void
1124 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1125 {
1126         if (_session == 0) {
1127                 return;
1128         }
1129
1130         TempoMap::BBTPointList::const_iterator i;
1131
1132         char buf[64];
1133         gint  n = 0;
1134         framepos_t pos;
1135         Timecode::BBT_Time next_beat;
1136         framepos_t next_beat_pos;
1137         uint32_t beats = 0;
1138         uint32_t tick = 0;
1139         uint32_t skip;
1140         uint32_t t;
1141         framepos_t frame_skip;
1142         double frame_skip_error;
1143         double bbt_position_of_helper;
1144         double accumulated_error;
1145         bool i_am_accented = false;
1146         bool helper_active = false;
1147         ArdourCanvas::Ruler::Mark mark;
1148
1149         ARDOUR::TempoMap::BBTPointList::const_iterator begin;
1150         ARDOUR::TempoMap::BBTPointList::const_iterator end;
1151
1152         compute_current_bbt_points (lower, upper, begin, end);
1153
1154         if (distance (begin, end) == 0) {
1155                 return;
1156         }
1157
1158         switch (bbt_ruler_scale) {
1159
1160         case bbt_show_beats:
1161                 beats = distance (begin, end);
1162                 bbt_nmarks = beats + 2;
1163
1164                 mark.label = "";
1165                 mark.position = lower;
1166                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1167                 marks.push_back (mark);
1168
1169                 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1170
1171                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1172                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1173                                 edit_last_mark_label (marks, buf);
1174                                 helper_active = true;
1175                         } else {
1176
1177                                 if ((*i).is_bar()) {
1178                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1179                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1180                                 } else if (((*i).beat % 2 == 1)) {
1181                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1182                                         buf[0] = '\0';
1183                                 } else {
1184                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1185                                         buf[0] = '\0';
1186                                 }
1187                                 mark.label = buf;
1188                                 mark.position = (*i).frame;
1189                                 marks.push_back (mark);
1190                                 n++;
1191                         }
1192                 }
1193                 break;
1194
1195         case bbt_show_ticks:
1196
1197                 beats = distance (begin, end);
1198                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1199
1200                 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1201                 
1202                 // could do marks.assign() here to preallocate
1203
1204                 mark.label = "";
1205                 mark.position = lower;
1206                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1207                 marks.push_back (mark);
1208
1209                 for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
1210
1211                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1212                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1213                                 edit_last_mark_label (marks, buf);
1214                                 helper_active = true;
1215                         } else {
1216
1217                                 if ((*i).is_bar()) {
1218                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1219                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1220                                 } else {
1221                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1222                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1223                                 }
1224                                 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1225                                         buf[0] = '\0';
1226                                 }
1227                                 mark.label =  buf;
1228                                 mark.position = (*i).frame;
1229                                 marks.push_back (mark);
1230                                 n++;
1231                         }
1232
1233                         /* Add the tick marks */
1234
1235                         /* Find the next beat */
1236                         next_beat.beats = (*i).beat;
1237                         next_beat.bars = (*i).bar;
1238                         next_beat.ticks = 0;
1239
1240                         if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1241                                   next_beat.beats += 1;
1242                         } else {
1243                                   next_beat.bars += 1;
1244                                   next_beat.beats = 1;
1245                         }
1246
1247                         next_beat_pos = _session->tempo_map().frame_time(next_beat);
1248
1249                         frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1250                         frame_skip_error -= frame_skip;
1251                         skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1252
1253                         pos = (*i).frame + frame_skip;
1254                         accumulated_error = frame_skip_error;
1255
1256                         tick = skip;
1257
1258                         for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1259
1260                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1261                                         i_am_accented = true;
1262                                 }
1263
1264                                 mark.label = "";
1265
1266                                 /* Error compensation for float to framepos_t*/
1267                                 accumulated_error += frame_skip_error;
1268                                 if (accumulated_error > 1) {
1269                                         pos += 1;
1270                                         accumulated_error -= 1.0f;
1271                                 }
1272
1273                                 mark.position = pos;
1274
1275                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1276                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1277                                 } else {
1278                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1279                                 }
1280                                 i_am_accented = false;
1281                                 marks.push_back (mark);
1282                                 n++;
1283                         }
1284                 }
1285
1286           break;
1287
1288         case bbt_show_ticks_detail:
1289
1290                 beats = distance (begin, end);
1291                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1292
1293                 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1294
1295                 mark.label = "";
1296                 mark.position = lower;
1297                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1298                 marks.push_back (mark);
1299
1300                 for (n = 1,   i = begin; n < bbt_nmarks && i != end; ++i) {
1301
1302                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1303                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1304                                 edit_last_mark_label (marks, buf);
1305                                 helper_active = true;
1306                         } else {
1307
1308                                 if ((*i).is_bar()) {
1309                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1310                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1311                                 } else {
1312                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1313                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1314                                 }
1315                                 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1316                                         buf[0] = '\0';
1317                                 }
1318                                 mark.label =  buf;
1319                                 mark.position = (*i).frame;
1320                                 marks.push_back (mark);
1321                                 n++;
1322                         }
1323
1324                         /* Add the tick marks */
1325
1326                         /* Find the next beat */
1327
1328                         next_beat.beats = (*i).beat;
1329                         next_beat.bars = (*i).bar;
1330
1331                         if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1332                                   next_beat.beats += 1;
1333                         } else {
1334                                   next_beat.bars += 1;
1335                                   next_beat.beats = 1;
1336                         }
1337
1338                         next_beat_pos = _session->tempo_map().frame_time(next_beat);
1339
1340                         frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1341                         frame_skip_error -= frame_skip;
1342                         skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1343
1344                         pos = (*i).frame + frame_skip;
1345                         accumulated_error = frame_skip_error;
1346
1347                         tick = skip;
1348
1349                         for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1350
1351                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1352                                         i_am_accented = true;
1353                                 }
1354
1355                                 if (i_am_accented && (pos > bbt_position_of_helper)){
1356                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1357                                 } else {
1358                                         buf[0] = '\0';
1359                                 }
1360
1361                                 mark.label = buf;
1362
1363                                 /* Error compensation for float to framepos_t*/
1364                                 accumulated_error += frame_skip_error;
1365                                 if (accumulated_error > 1) {
1366                                         pos += 1;
1367                                         accumulated_error -= 1.0f;
1368                                 }
1369
1370                                 mark.position = pos;
1371
1372                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1373                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1374                                 } else {
1375                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1376                                 }
1377                                 i_am_accented = false;
1378                                 n++;
1379                         }
1380                 }
1381
1382           break;
1383
1384         case bbt_show_ticks_super_detail:
1385
1386                 beats = distance (begin, end);
1387                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1388
1389                 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1390
1391                 mark.label = "";
1392                 mark.position = lower;
1393                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1394                 marks.push_back (mark);
1395
1396                 for (n = 1,   i = begin; n < bbt_nmarks && i != end; ++i) {
1397
1398                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1399                                   snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1400                                   edit_last_mark_label (marks, buf);
1401                                   helper_active = true;
1402                         } else {
1403
1404                                   if ((*i).is_bar()) {
1405                                           mark.style = ArdourCanvas::Ruler::Mark::Major;
1406                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1407                                   } else {
1408                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1409                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1410                                   }
1411                                   if (((*i).frame < bbt_position_of_helper) && helper_active) {
1412                                           buf[0] = '\0';
1413                                   }
1414                                   mark.label =  buf;
1415                                   mark.position = (*i).frame;
1416                                   marks.push_back (mark);
1417                                   n++;
1418                         }
1419
1420                         /* Add the tick marks */
1421
1422                         /* Find the next beat */
1423
1424                         next_beat.beats = (*i).beat;
1425                         next_beat.bars = (*i).bar;
1426
1427                         if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
1428                                   next_beat.beats += 1;
1429                         } else {
1430                                   next_beat.bars += 1;
1431                                   next_beat.beats = 1;
1432                         }
1433
1434                         next_beat_pos = _session->tempo_map().frame_time(next_beat);
1435
1436                         frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1437                         frame_skip_error -= frame_skip;
1438                         skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
1439
1440                         pos = (*i).frame + frame_skip;
1441                         accumulated_error = frame_skip_error;
1442
1443                         tick = skip;
1444
1445                         for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1446
1447                                   if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1448                                           i_am_accented = true;
1449                                   }
1450
1451                                   if (pos > bbt_position_of_helper) {
1452                                           snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1453                                   } else {
1454                                           buf[0] = '\0';
1455                                   }
1456
1457                                   mark.label = buf;
1458
1459                                   /* Error compensation for float to framepos_t*/
1460                                   accumulated_error += frame_skip_error;
1461                                   if (accumulated_error > 1) {
1462                                           pos += 1;
1463                                           accumulated_error -= 1.0f;
1464                                   }
1465
1466                                   mark.position = pos;
1467
1468                                   if ((bbt_beat_subdivision > 4) && i_am_accented) {
1469                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1470                                   } else {
1471                                           mark.style = ArdourCanvas::Ruler::Mark::Micro;
1472                                   }
1473                                   i_am_accented = false;
1474                                   marks.push_back (mark);
1475                                   n++;
1476                         }
1477                 }
1478
1479           break;
1480
1481         case bbt_over:
1482                         bbt_nmarks = 1;
1483                         snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1484                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1485                         mark.label = buf;
1486                         mark.position = lower;
1487                         marks.push_back (mark);
1488
1489           break;
1490
1491         case bbt_show_64:
1492                         bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1493                         for (n = 0,   i = begin; i != end && n < bbt_nmarks; i++) {
1494                                 if ((*i).is_bar()) {
1495                                         if ((*i).bar % 64 == 1) {
1496                                                 if ((*i).bar % 256 == 1) {
1497                                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1498                                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1499                                                 } else {
1500                                                         buf[0] = '\0';
1501                                                         if ((*i).bar % 256 == 129)  {
1502                                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1503                                                         } else {
1504                                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1505                                                         }
1506                                                 }
1507                                                 mark.label = buf;
1508                                                 mark.position = (*i).frame;
1509                                                 marks.push_back (mark);
1510                                                 ++n;
1511                                         }
1512                                 }
1513                         }
1514                         break;
1515
1516         case bbt_show_16:
1517                 bbt_nmarks = (bbt_bars / 16) + 1;
1518                 for (n = 0,  i = begin; i != end && n < bbt_nmarks; i++) {
1519                         if ((*i).is_bar()) {
1520                           if ((*i).bar % 16 == 1) {
1521                                 if ((*i).bar % 64 == 1) {
1522                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1523                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1524                                 } else {
1525                                         buf[0] = '\0';
1526                                         if ((*i).bar % 64 == 33)  {
1527                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1528                                         } else {
1529                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1530                                         }
1531                                 }
1532                                 mark.label = buf;
1533                                 mark.position = (*i).frame;
1534                                 marks.push_back (mark);
1535                                 ++n;
1536                           }
1537                         }
1538                 }
1539           break;
1540
1541         case bbt_show_4:
1542                 bbt_nmarks = (bbt_bars / 4) + 1;
1543                 for (n = 0,   i = begin; i != end && n < bbt_nmarks; ++i) {
1544                         if ((*i).is_bar()) {
1545                           if ((*i).bar % 4 == 1) {
1546                                 if ((*i).bar % 16 == 1) {
1547                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1548                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1549                                 } else {
1550                                         buf[0] = '\0';
1551                                         if ((*i).bar % 16 == 9)  {
1552                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1553                                         } else {
1554                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1555                                         }
1556                                 }
1557                                 mark.label = buf;
1558                                 mark.position = (*i).frame;
1559                                 marks.push_back (mark);
1560                                 ++n;
1561                           }
1562                         }
1563                 }
1564           break;
1565
1566         case bbt_show_1:
1567 //      default:
1568                 bbt_nmarks = bbt_bars + 2;
1569                 for (n = 0,  i = begin; i != end && n < bbt_nmarks; ++i) {
1570                         if ((*i).is_bar()) {
1571                           if ((*i).bar % 4 == 1) {
1572                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1573                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1574                           } else {
1575                                   buf[0] = '\0';
1576                                   if ((*i).bar % 4 == 3)  {
1577                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1578                                   } else {
1579                                           mark.style = ArdourCanvas::Ruler::Mark::Micro;
1580                                   }
1581                           }
1582                           mark.label = buf;
1583                           mark.position = (*i).frame;
1584                           marks.push_back (mark);
1585                           ++n;
1586                         }
1587                 }
1588                 break;
1589
1590         }
1591 }
1592
1593 void
1594 Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
1595 {
1596         _samples_ruler_interval = (upper - lower) / 5;
1597 }
1598
1599 void
1600 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1601 {
1602         framepos_t pos;
1603         framepos_t const ilower = (framepos_t) floor (lower);
1604         gchar buf[16];
1605         gint nmarks;
1606         gint n;
1607         ArdourCanvas::Ruler::Mark mark;
1608
1609         if (_session == 0) {
1610                 return;
1611         }
1612
1613         nmarks = 5;
1614         for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1615                 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1616                 mark.label = buf;
1617                 mark.position = pos;
1618                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1619                 marks.push_back (mark);
1620         }
1621 }
1622
1623 static void
1624 sample_to_clock_parts ( framepos_t sample,
1625                         framepos_t sample_rate,
1626                         long *hrs_p,
1627                         long *mins_p,
1628                         long *secs_p,
1629                         long *millisecs_p)
1630
1631 {
1632         framepos_t left;
1633         long hrs;
1634         long mins;
1635         long secs;
1636         long millisecs;
1637
1638         left = sample;
1639         hrs = left / (sample_rate * 60 * 60 * 1000);
1640         left -= hrs * sample_rate * 60 * 60 * 1000;
1641         mins = left / (sample_rate * 60 * 1000);
1642         left -= mins * sample_rate * 60 * 1000;
1643         secs = left / (sample_rate * 1000);
1644         left -= secs * sample_rate * 1000;
1645         millisecs = left / sample_rate;
1646
1647         *millisecs_p = millisecs;
1648         *secs_p = secs;
1649         *mins_p = mins;
1650         *hrs_p = hrs;
1651
1652         return;
1653 }
1654
1655 void
1656 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
1657 {
1658         framepos_t fr;
1659         framepos_t spacer;
1660
1661         if (_session == 0) {
1662                 return;
1663         }
1664
1665         fr = _session->frame_rate() * 1000;
1666
1667         /* to prevent 'flashing' */
1668         if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
1669                 lower -= spacer;
1670         } else {
1671                 lower = 0;
1672         }
1673         upper += spacer;
1674         framecnt_t const range = (upper - lower) * 1000;
1675
1676         if (range <  (fr / 50)) {
1677                 minsec_mark_interval =  fr / 1000; /* show 1/1000 seconds */
1678                 minsec_ruler_scale = minsec_show_frames;
1679                 minsec_mark_modulo = 10;
1680         } else if (range <= (fr / 10)) { /* 0-0.1 second */
1681                 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1682                 minsec_ruler_scale = minsec_show_frames;
1683                 minsec_mark_modulo = 10;
1684         } else if (range <= (fr / 2)) { /* 0-0.5 second */
1685                 minsec_mark_interval = fr / 100;  /* show 1/100 seconds */
1686                 minsec_ruler_scale = minsec_show_frames;
1687                 minsec_mark_modulo = 100;
1688         } else if (range <= fr) { /* 0-1 second */
1689                 minsec_mark_interval = fr / 10;  /* show 1/10 seconds */
1690                 minsec_ruler_scale = minsec_show_frames;
1691                 minsec_mark_modulo = 200;
1692         } else if (range <= 2 * fr) { /* 1-2 seconds */
1693                 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1694                 minsec_ruler_scale = minsec_show_frames;
1695                 minsec_mark_modulo = 500;
1696         } else if (range <= 8 * fr) { /* 2-5 seconds */
1697                 minsec_mark_interval =  fr / 5; /* show 2 seconds */
1698                 minsec_ruler_scale = minsec_show_frames;
1699                 minsec_mark_modulo = 1000;
1700         } else if (range <= 16 * fr) { /* 8-16 seconds */
1701                 minsec_mark_interval =  fr; /* show 1 seconds */
1702                 minsec_ruler_scale = minsec_show_seconds;
1703                 minsec_mark_modulo = 2;
1704         } else if (range <= 30 * fr) { /* 10-30 seconds */
1705                 minsec_mark_interval =  fr; /* show 1 seconds */
1706                 minsec_ruler_scale = minsec_show_seconds;
1707                 minsec_mark_modulo = 5;
1708         } else if (range <= 60 * fr) { /* 30-60 seconds */
1709                 minsec_mark_interval = fr; /* show 1 seconds */
1710                 minsec_ruler_scale = minsec_show_seconds;
1711                 minsec_mark_modulo = 5;
1712         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1713                 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1714                 minsec_ruler_scale = minsec_show_seconds;
1715                 minsec_mark_modulo = 3;
1716         } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1717                 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1718                 minsec_ruler_scale = minsec_show_seconds;
1719                 minsec_mark_modulo = 30;
1720         } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1721                 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1722                 minsec_ruler_scale = minsec_show_seconds;
1723                 minsec_mark_modulo = 120;
1724         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1725                 minsec_mark_interval =  60 * fr; /* show 1 minute */
1726                 minsec_ruler_scale = minsec_show_minutes;
1727                 minsec_mark_modulo = 5;
1728         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1729                 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1730                 minsec_ruler_scale = minsec_show_minutes;
1731                 minsec_mark_modulo = 10;
1732         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1733                 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1734                 minsec_ruler_scale = minsec_show_minutes;
1735                 minsec_mark_modulo = 30;
1736         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1737                 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1738                 minsec_ruler_scale = minsec_show_minutes;
1739                 minsec_mark_modulo = 60;
1740         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1741                 minsec_mark_interval =  60 * 60 * fr; /* show 60 minutes */
1742                 minsec_ruler_scale = minsec_show_hours;
1743                 minsec_mark_modulo = 2;
1744         } else {
1745
1746                 /* not possible if framepos_t is a 32 bit quantity */
1747
1748                 minsec_mark_interval = 4 * 60 * 60 * fr; /* show 4 hrs */
1749         }
1750         minsec_nmarks = 2 + (range / minsec_mark_interval);
1751 }
1752
1753 void
1754 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1755 {
1756         framepos_t pos;
1757         framepos_t spacer;
1758         long hrs, mins, secs, millisecs;
1759         gchar buf[16];
1760         gint n;
1761         ArdourCanvas::Ruler::Mark mark;
1762
1763         if (_session == 0) {
1764                 return;
1765         }
1766
1767         /* to prevent 'flashing' */
1768         if (lower > (spacer = (framepos_t) (128 * Editor::get_current_zoom ()))) {
1769                 lower = lower - spacer;
1770         } else {
1771                 lower = 0;
1772         }
1773
1774         pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1775         switch (minsec_ruler_scale) {
1776         case minsec_show_seconds:
1777                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1778                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1779                         if (secs % minsec_mark_modulo == 0) {
1780                                 if (secs == 0) {
1781                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1782                                 } else {
1783                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1784                                 }
1785                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1786                         } else {
1787                                 buf[0] = '\0';
1788                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1789                         }
1790                         mark.label = buf;
1791                         mark.position = pos/1000.0;
1792                         marks.push_back (mark);
1793                 }
1794           break;
1795         case minsec_show_minutes:
1796                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1797                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1798                         if (mins % minsec_mark_modulo == 0) {
1799                                 if (mins == 0) {
1800                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1801                                 } else {
1802                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1803                                 }
1804                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1805                         } else {
1806                                 buf[0] = '\0';
1807                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1808                         }
1809                         mark.label = buf;
1810                         mark.position = pos/1000.0;
1811                         marks.push_back (mark);
1812                 }
1813           break;
1814         case minsec_show_hours:
1815                  for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1816                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1817                         if (hrs % minsec_mark_modulo == 0) {
1818                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1819                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1820                         } else {
1821                                 buf[0] = '\0';
1822                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1823                         }
1824                         mark.label = buf;
1825                         mark.position = pos/1000.0;
1826                         marks.push_back (mark);
1827                 }
1828               break;
1829         case minsec_show_frames:
1830                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1831                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1832                         if (millisecs % minsec_mark_modulo == 0) {
1833                                 if (millisecs == 0) {
1834                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1835                                 } else {
1836                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1837                                 }
1838                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1839                         } else {
1840                                 buf[0] = '\0';
1841                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1842                         }
1843                         mark.label = buf;
1844                         mark.position = pos/1000.0;
1845                         marks.push_back (mark);
1846                 }
1847           break;
1848         }
1849 }