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