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