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