start using ActionMap in preference to ActionManager
[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 (samplepos_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->set_property (X_("timecode"), ruler_timecode_action->get_active());
298         node->set_property (X_("bbt"), ruler_bbt_action->get_active());
299         node->set_property (X_("samples"), ruler_samples_action->get_active());
300         node->set_property (X_("minsec"), ruler_minsec_action->get_active());
301         node->set_property (X_("tempo"), ruler_tempo_action->get_active());
302         node->set_property (X_("meter"), ruler_meter_action->get_active());
303         node->set_property (X_("marker"), ruler_marker_action->get_active());
304         node->set_property (X_("rangemarker"), ruler_range_action->get_active());
305         node->set_property (X_("transportmarker"), ruler_loop_punch_action->get_active());
306         node->set_property (X_("cdmarker"), ruler_cd_marker_action->get_active());
307         node->set_property (X_("videotl"), ruler_video_action->get_active());
308
309         _session->add_extra_xml (*node);
310 }
311
312 void
313 Editor::restore_ruler_visibility ()
314 {
315         XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
316
317         no_ruler_shown_update = true;
318
319         bool yn;
320         if (node) {
321                 if (node->get_property ("timecode", yn)) {
322                         ruler_timecode_action->set_active (yn);
323                 }
324                 if (node->get_property ("bbt", yn)) {
325                         ruler_bbt_action->set_active (yn);
326                 }
327                 if (node->get_property ("samples", yn)) {
328                         ruler_samples_action->set_active (yn);
329                 }
330                 if (node->get_property ("minsec", yn)) {
331                         ruler_minsec_action->set_active (yn);
332                 }
333                 if (node->get_property ("tempo", yn)) {
334                         ruler_tempo_action->set_active (yn);
335                 }
336                 if (node->get_property ("meter", yn)) {
337                         ruler_meter_action->set_active (yn);
338                 }
339                 if (node->get_property ("marker", yn)) {
340                         ruler_marker_action->set_active (yn);
341                 }
342                 if (node->get_property ("rangemarker", yn)) {
343                         ruler_range_action->set_active (yn);
344                 }
345                 if (node->get_property ("transportmarker", yn)) {
346                         ruler_loop_punch_action->set_active (yn);
347                 }
348
349                 if (node->get_property ("cdmarker", yn)) {
350                                 ruler_cd_marker_action->set_active (yn);
351                 } else {
352                         // this _session doesn't yet know about the cdmarker ruler
353                         // as a benefit to the user who doesn't know the feature exists, show the ruler if
354                         // any cd marks exist
355                         ruler_cd_marker_action->set_active (false);
356                         const Locations::LocationList & locs = _session->locations()->list();
357                         for (Locations::LocationList::const_iterator i = locs.begin(); i != locs.end(); ++i) {
358                                 if ((*i)->is_cd_marker()) {
359                                         ruler_cd_marker_action->set_active (true);
360                                         break;
361                                 }
362                         }
363                 }
364
365                 if (node->get_property ("videotl", yn)) {
366                         ruler_video_action->set_active (yn);
367                 }
368
369         }
370
371         no_ruler_shown_update = false;
372         update_ruler_visibility ();
373 }
374
375 void
376 Editor::update_ruler_visibility ()
377 {
378         int visible_timebars = 0;
379
380         if (no_ruler_shown_update) {
381                 return;
382         }
383
384         /* the order of the timebars is fixed, so we have to go through each one
385          * and adjust its position depending on what is shown.
386          *
387          * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
388          * loop/punch, cd markers, location markers
389          */
390
391         double tbpos = 0.0;
392         double tbgpos = 0.0;
393         double old_unit_pos;
394
395 #ifdef __APPLE__
396         /* gtk update probs require this (damn) */
397         meter_label.hide();
398         tempo_label.hide();
399         range_mark_label.hide();
400         transport_mark_label.hide();
401         cd_mark_label.hide();
402         mark_label.hide();
403         videotl_label.hide();
404 #endif
405
406         if (ruler_minsec_action->get_active()) {
407                 old_unit_pos = minsec_ruler->position().y;
408                 if (tbpos != old_unit_pos) {
409                         minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
410                 }
411                 minsec_ruler->show();
412                 minsec_label.show();
413                 tbpos += timebar_height;
414                 tbgpos += timebar_height;
415                 visible_timebars++;
416         } else {
417                 minsec_ruler->hide();
418                 minsec_label.hide();
419         }
420
421         if (ruler_timecode_action->get_active()) {
422                 old_unit_pos = timecode_ruler->position().y;
423                 if (tbpos != old_unit_pos) {
424                         timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
425                 }
426                 timecode_ruler->show();
427                 timecode_label.show();
428                 tbpos += timebar_height;
429                 tbgpos += timebar_height;
430                 visible_timebars++;
431         } else {
432                 timecode_ruler->hide();
433                 timecode_label.hide();
434         }
435
436         if (ruler_samples_action->get_active()) {
437                 old_unit_pos = samples_ruler->position().y;
438                 if (tbpos != old_unit_pos) {
439                         samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
440                 }
441                 samples_ruler->show();
442                 samples_label.show();
443                 tbpos += timebar_height;
444                 tbgpos += timebar_height;
445                 visible_timebars++;
446         } else {
447                 samples_ruler->hide();
448                 samples_label.hide();
449         }
450
451         if (ruler_bbt_action->get_active()) {
452                 old_unit_pos = bbt_ruler->position().y;
453                 if (tbpos != old_unit_pos) {
454                         bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
455                 }
456                 bbt_ruler->show();
457                 bbt_label.show();
458                 tbpos += timebar_height;
459                 tbgpos += timebar_height;
460                 visible_timebars++;
461         } else {
462                 bbt_ruler->hide();
463                 bbt_label.hide();
464         }
465
466         if (ruler_meter_action->get_active()) {
467                 old_unit_pos = meter_group->position().y;
468                 if (tbpos != old_unit_pos) {
469                         meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
470                 }
471                 meter_group->show();
472                 meter_label.show();
473                 tbpos += timebar_height;
474                 tbgpos += timebar_height;
475                 visible_timebars++;
476         } else {
477                 meter_group->hide();
478                 meter_label.hide();
479         }
480
481         if (ruler_tempo_action->get_active()) {
482                 old_unit_pos = tempo_group->position().y;
483                 if (tbpos != old_unit_pos) {
484                         tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
485                 }
486                 tempo_group->show();
487                 tempo_label.show();
488                 tbpos += timebar_height;
489                 tbgpos += timebar_height;
490                 visible_timebars++;
491         } else {
492                 tempo_group->hide();
493                 tempo_label.hide();
494         }
495
496         if (ruler_range_action->get_active()) {
497                 old_unit_pos = range_marker_group->position().y;
498                 if (tbpos != old_unit_pos) {
499                         range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
500                 }
501                 range_marker_group->show();
502                 range_mark_label.show();
503
504                 tbpos += timebar_height;
505                 tbgpos += timebar_height;
506                 visible_timebars++;
507         } else {
508                 range_marker_group->hide();
509                 range_mark_label.hide();
510         }
511
512         if (ruler_loop_punch_action->get_active()) {
513                 old_unit_pos = transport_marker_group->position().y;
514                 if (tbpos != old_unit_pos) {
515                         transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
516                 }
517                 transport_marker_group->show();
518                 transport_mark_label.show();
519                 tbpos += timebar_height;
520                 tbgpos += timebar_height;
521                 visible_timebars++;
522         } else {
523                 transport_marker_group->hide();
524                 transport_mark_label.hide();
525         }
526
527         if (ruler_cd_marker_action->get_active()) {
528                 old_unit_pos = cd_marker_group->position().y;
529                 if (tbpos != old_unit_pos) {
530                         cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
531                 }
532                 cd_marker_group->show();
533                 cd_mark_label.show();
534                 tbpos += timebar_height;
535                 tbgpos += timebar_height;
536                 visible_timebars++;
537                 // make sure all cd markers show up in their respective places
538                 update_cd_marker_display();
539         } else {
540                 cd_marker_group->hide();
541                 cd_mark_label.hide();
542                 // make sure all cd markers show up in their respective places
543                 update_cd_marker_display();
544         }
545
546         if (ruler_marker_action->get_active()) {
547                 old_unit_pos = marker_group->position().y;
548                 if (tbpos != old_unit_pos) {
549                         marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
550                 }
551                 marker_group->show();
552                 mark_label.show();
553                 tbpos += timebar_height;
554                 tbgpos += timebar_height;
555                 visible_timebars++;
556         } else {
557                 marker_group->hide();
558                 mark_label.hide();
559         }
560
561         if (ruler_video_action->get_active()) {
562                 old_unit_pos = videotl_group->position().y;
563                 if (tbpos != old_unit_pos) {
564                         videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
565                 }
566                 videotl_group->show();
567                 videotl_label.show();
568                 tbpos += timebar_height * videotl_bar_height;
569                 tbgpos += timebar_height * videotl_bar_height;
570                 visible_timebars+=videotl_bar_height;
571                 queue_visual_videotimeline_update();
572         } else {
573                 videotl_group->hide();
574                 videotl_label.hide();
575                 update_video_timeline(true);
576         }
577
578         time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
579
580         /* move hv_scroll_group (trackviews) to the end of the timebars
581          */
582
583         hv_scroll_group->set_y_position (timebar_height * visible_timebars);
584
585         compute_fixed_ruler_scale ();
586         update_fixed_rulers();
587         redisplay_grid (false);
588
589         /* Changing ruler visibility means that any lines on markers might need updating */
590         for (LocationMarkerMap::iterator i = location_markers.begin(); i != location_markers.end(); ++i) {
591                 i->second->setup_lines ();
592         }
593 }
594
595 void
596 Editor::update_just_timecode ()
597 {
598         ENSURE_GUI_THREAD (*this, &Editor::update_just_timecode)
599
600         if (_session == 0) {
601                 return;
602         }
603
604         samplepos_t rightmost_sample = _leftmost_sample + current_page_samples();
605
606         if (ruler_timecode_action->get_active()) {
607                 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
608         }
609 }
610
611 void
612 Editor::compute_fixed_ruler_scale ()
613 {
614         if (_session == 0) {
615                 return;
616         }
617
618         if (ruler_timecode_action->get_active()) {
619                 set_timecode_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
620         }
621
622         if (ruler_minsec_action->get_active()) {
623                 set_minsec_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
624         }
625
626         if (ruler_samples_action->get_active()) {
627                 set_samples_ruler_scale (_leftmost_sample, _leftmost_sample + current_page_samples());
628         }
629 }
630
631 void
632 Editor::update_fixed_rulers ()
633 {
634         samplepos_t rightmost_sample;
635
636         if (_session == 0) {
637                 return;
638         }
639
640         compute_fixed_ruler_scale ();
641
642         _timecode_metric->units_per_pixel = samples_per_pixel;
643         _samples_metric->units_per_pixel = samples_per_pixel;
644         _minsec_metric->units_per_pixel = samples_per_pixel;
645
646         rightmost_sample = _leftmost_sample + current_page_samples();
647
648         /* these force a redraw, which in turn will force execution of the metric callbacks
649            to compute the relevant ticks to display.
650         */
651
652         if (ruler_timecode_action->get_active()) {
653                 timecode_ruler->set_range (_leftmost_sample, rightmost_sample);
654         }
655
656         if (ruler_samples_action->get_active()) {
657                 samples_ruler->set_range (_leftmost_sample, rightmost_sample);
658         }
659
660         if (ruler_minsec_action->get_active()) {
661                 minsec_ruler->set_range (_leftmost_sample, rightmost_sample);
662         }
663 }
664
665 void
666 Editor::update_tempo_based_rulers ()
667 {
668         if (_session == 0) {
669                 return;
670         }
671
672         _bbt_metric->units_per_pixel = samples_per_pixel;
673
674         if (ruler_bbt_action->get_active()) {
675                 bbt_ruler->set_range (_leftmost_sample, _leftmost_sample+current_page_samples());
676         }
677 }
678
679
680 void
681 Editor::set_timecode_ruler_scale (samplepos_t lower, samplepos_t upper)
682 {
683         using namespace std;
684
685         samplepos_t spacer;
686         samplepos_t fr;
687
688         if (_session == 0) {
689                 return;
690         }
691
692         fr = _session->sample_rate();
693
694         if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
695                 lower = lower - spacer;
696         } else {
697                 lower = 0;
698         }
699
700         upper = upper + spacer;
701         samplecnt_t const range = upper - lower;
702
703         if (range < (2 * _session->samples_per_timecode_frame())) { /* 0 - 2 samples */
704                 timecode_ruler_scale = timecode_show_bits;
705                 timecode_mark_modulo = 20;
706                 timecode_nmarks = 2 + (2 * _session->config.get_subframes_per_frame());
707         } else if (range <= (fr / 4)) { /* 2 samples - 0.250 second */
708                 timecode_ruler_scale = timecode_show_samples;
709                 timecode_mark_modulo = 1;
710                 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
711         } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
712                 timecode_ruler_scale = timecode_show_samples;
713                 timecode_mark_modulo = 2;
714                 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
715         } else if (range <= fr) { /* 0.5-1 second */
716                 timecode_ruler_scale = timecode_show_samples;
717                 timecode_mark_modulo = 5;
718                 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
719         } else if (range <= 2 * fr) { /* 1-2 seconds */
720                 timecode_ruler_scale = timecode_show_samples;
721                 timecode_mark_modulo = 10;
722                 timecode_nmarks = 2 + (range / (samplepos_t)_session->samples_per_timecode_frame());
723         } else if (range <= 8 * fr) { /* 2-8 seconds */
724                 timecode_ruler_scale = timecode_show_seconds;
725                 timecode_mark_modulo = 1;
726                 timecode_nmarks = 2 + (range / fr);
727         } else if (range <= 16 * fr) { /* 8-16 seconds */
728                 timecode_ruler_scale = timecode_show_seconds;
729                 timecode_mark_modulo = 2;
730                 timecode_nmarks = 2 + (range / fr);
731         } else if (range <= 30 * fr) { /* 16-30 seconds */
732                 timecode_ruler_scale = timecode_show_seconds;
733                 timecode_mark_modulo = 5;
734                 timecode_nmarks = 2 + (range / fr);
735         } else if (range <= 60 * fr) { /* 30-60 seconds */
736                 timecode_ruler_scale = timecode_show_seconds;
737                 timecode_mark_modulo = 5;
738                 timecode_nmarks = 2 + (range / fr);
739         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
740                 timecode_ruler_scale = timecode_show_seconds;
741                 timecode_mark_modulo = 15;
742                 timecode_nmarks = 2 + (range / fr);
743         } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
744                 timecode_ruler_scale = timecode_show_seconds;
745                 timecode_mark_modulo = 30;
746                 timecode_nmarks = 2 + (range / fr);
747         } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
748                 timecode_ruler_scale = timecode_show_minutes;
749                 timecode_mark_modulo = 2;
750                 timecode_nmarks = 2 + 10;
751         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
752                 timecode_ruler_scale = timecode_show_minutes;
753                 timecode_mark_modulo = 5;
754                 timecode_nmarks = 2 + 30;
755         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
756                 timecode_ruler_scale = timecode_show_minutes;
757                 timecode_mark_modulo = 10;
758                 timecode_nmarks = 2 + 60;
759         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
760                 timecode_ruler_scale = timecode_show_minutes;
761                 timecode_mark_modulo = 30;
762                 timecode_nmarks = 2 + (60 * 4);
763         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
764                 timecode_ruler_scale = timecode_show_hours;
765                 timecode_mark_modulo = 1;
766                 timecode_nmarks = 2 + 8;
767         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
768                 timecode_ruler_scale = timecode_show_hours;
769                 timecode_mark_modulo = 1;
770                 timecode_nmarks = 2 + 24;
771         } else {
772
773                 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
774                 const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
775
776                 /* Normally we do not need to know anything about the width of the canvas
777                    to set the ruler scale, because the caller has already determined
778                    the width and set lower + upper arguments to this function to match that.
779
780                    But in this case, where the range defined by lower and uppper can vary
781                    substantially (basically anything from 24hrs+ to several billion years)
782                    trying to decide which tick marks to show does require us to know
783                    about the available width.
784                 */
785
786                 timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
787                 timecode_ruler_scale = timecode_show_many_hours;
788                 timecode_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
789         }
790 }
791
792 void
793 Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
794 {
795         samplepos_t pos;
796         samplecnt_t spacer;
797         Timecode::Time timecode;
798         gchar buf[16];
799         gint n;
800         ArdourCanvas::Ruler::Mark mark;
801
802         if (_session == 0) {
803                 return;
804         }
805
806         if (lower > (spacer = (samplecnt_t)(128 * Editor::get_current_zoom ()))) {
807                 lower = lower - spacer;
808         } else {
809                 lower = 0;
810         }
811
812         pos = (samplecnt_t) floor (lower);
813
814         switch (timecode_ruler_scale) {
815         case timecode_show_bits:
816                 // Find timecode time of this sample (pos) with subframe accuracy
817                 _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */);
818                 for (n = 0; n < timecode_nmarks; n++) {
819                         _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */);
820                         if ((timecode.subframes % timecode_mark_modulo) == 0) {
821                                 if (timecode.subframes == 0) {
822                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
823                                         snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
824                                 } else {
825                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
826                                         snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
827                                 }
828                         } else {
829                                 snprintf (buf, sizeof(buf)," ");
830                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
831                         }
832                         mark.label = buf;
833                         mark.position = pos;
834                         marks.push_back (mark);
835                         // Increment subframes by one
836                         Timecode::increment_subframes (timecode, _session->config.get_subframes_per_frame());
837                 }
838                 break;
839
840         case timecode_show_samples:
841                 // Find timecode time of this sample (pos)
842                 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
843                 // Go to next whole sample down
844                 Timecode::frames_floot (timecode);
845                 for (n = 0; n < timecode_nmarks; n++) {
846                         _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
847                         if ((timecode.frames % timecode_mark_modulo) == 0) {
848                                 if (timecode.frames == 0) {
849                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
850                                 } else {
851                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
852                                 }
853                                 mark.position = pos;
854                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
855                         } else {
856                                 snprintf (buf, sizeof(buf)," ");
857                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
858                                 mark.position = pos;
859                         }
860                         mark.label = buf;
861                         marks.push_back (mark);
862                         Timecode::increment (timecode, _session->config.get_subframes_per_frame());
863                 }
864                 break;
865
866         case timecode_show_seconds:
867                 // Find timecode time of this sample (pos)
868                 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
869                 // Go to next whole second down
870                 Timecode::seconds_floor (timecode);
871                 for (n = 0; n < timecode_nmarks; n++) {
872                         _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
873                         if ((timecode.seconds % timecode_mark_modulo) == 0) {
874                                 if (timecode.seconds == 0) {
875                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
876                                         mark.position = pos;
877                                 } else {
878                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
879                                         mark.position = pos;
880                                 }
881                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
882                         } else {
883                                 snprintf (buf, sizeof(buf)," ");
884                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
885                                 mark.position = pos;
886                         }
887                         mark.label = buf;
888                         marks.push_back (mark);
889                         Timecode::increment_seconds (timecode, _session->config.get_subframes_per_frame());
890                 }
891                 break;
892
893         case timecode_show_minutes:
894                 //Find timecode time of this sample (pos)
895                 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
896                 // Go to next whole minute down
897                 Timecode::minutes_floor (timecode);
898                 for (n = 0; n < timecode_nmarks; n++) {
899                         _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
900                         if ((timecode.minutes % timecode_mark_modulo) == 0) {
901                                 if (timecode.minutes == 0) {
902                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
903                                 } else {
904                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
905                                 }
906                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
907                         } else {
908                                 snprintf (buf, sizeof(buf)," ");
909                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
910                         }
911                         mark.label = buf;
912                         mark.position = pos;
913                         marks.push_back (mark);
914                         Timecode::increment_minutes (timecode, _session->config.get_subframes_per_frame());
915                 }
916                 break;
917         case timecode_show_hours:
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 hour down
921                 Timecode::hours_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.hours % timecode_mark_modulo) == 0) {
925                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
926                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
927                         } else {
928                                 snprintf (buf, sizeof(buf)," ");
929                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
930                         }
931                         mark.label = buf;
932                         mark.position = pos;
933                         marks.push_back (mark);
934                         Timecode::increment_hours (timecode, _session->config.get_subframes_per_frame());
935                 }
936                 break;
937         case timecode_show_many_hours:
938                 // Find timecode time of this sample (pos)
939                 _session->sample_to_timecode (pos, timecode, true /* use_offset */, false /* use_subframes */);
940                 // Go to next whole hour down
941                 Timecode::hours_floor (timecode);
942
943                 for (n = 0; n < timecode_nmarks;) {
944                         _session->timecode_to_sample (timecode, pos, true /* use_offset */, false /* use_subframes */);
945                         if ((timecode.hours % timecode_mark_modulo) == 0) {
946                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
947                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
948                                 mark.label = buf;
949                                 mark.position = pos;
950                                 marks.push_back (mark);
951                                 ++n;
952                         }
953                         /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
954                          * and doing it 1 hour at a time is just stupid (and slow).
955                          */
956                         timecode.hours += timecode_mark_modulo - (timecode.hours % timecode_mark_modulo);
957                 }
958                 break;
959         }
960 }
961
962 void
963 Editor::compute_bbt_ruler_scale (samplepos_t lower, samplepos_t upper)
964 {
965         if (_session == 0) {
966                 return;
967         }
968
969         std::vector<TempoMap::BBTPoint>::const_iterator i;
970         Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
971         double floor_lower_beat = floor(max (0.0, _session->tempo_map().beat_at_sample (lower)));
972
973         if (floor_lower_beat < 0.0) {
974                 floor_lower_beat = 0.0;
975         }
976
977         const samplecnt_t beat_before_lower_pos = _session->tempo_map().sample_at_beat (floor_lower_beat);
978         const samplecnt_t beat_after_upper_pos = _session->tempo_map().sample_at_beat (floor (max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0);
979
980         _session->bbt_time (beat_before_lower_pos, lower_beat);
981         _session->bbt_time (beat_after_upper_pos, upper_beat);
982         uint32_t beats = 0;
983
984         bbt_accent_modulo = 1;
985         bbt_bar_helper_on = false;
986         bbt_bars = 0;
987         bbt_nmarks = 1;
988
989         bbt_ruler_scale =  bbt_show_many;
990
991         switch (_grid_type) {
992         case GridTypeBeatDiv2:
993                 bbt_beat_subdivision = 2;
994                 break;
995         case GridTypeBeatDiv3:
996                 bbt_beat_subdivision = 3;
997                 break;
998         case GridTypeBeatDiv4:
999                 bbt_beat_subdivision = 4;
1000                 break;
1001         case GridTypeBeatDiv5:
1002                 bbt_beat_subdivision = 5;
1003                 bbt_accent_modulo = 2; // XXX YIKES
1004                 break;
1005         case GridTypeBeatDiv6:
1006                 bbt_beat_subdivision = 3;
1007                 bbt_accent_modulo = 2; // XXX YIKES
1008                 break;
1009         case GridTypeBeatDiv7:
1010                 bbt_beat_subdivision = 7;
1011                 bbt_accent_modulo = 2; // XXX YIKES
1012                 break;
1013         case GridTypeBeatDiv8:
1014                 bbt_beat_subdivision = 4;
1015                 bbt_accent_modulo = 2;
1016                 break;
1017         case GridTypeBeatDiv10:
1018                 bbt_beat_subdivision = 5;
1019                 bbt_accent_modulo = 2; // XXX YIKES
1020                 break;
1021         case GridTypeBeatDiv12:
1022                 bbt_beat_subdivision = 3;
1023                 bbt_accent_modulo = 3;
1024                 break;
1025         case GridTypeBeatDiv14:
1026                 bbt_beat_subdivision = 7;
1027                 bbt_accent_modulo = 3; // XXX YIKES!
1028                 break;
1029         case GridTypeBeatDiv16:
1030                 bbt_beat_subdivision = 4;
1031                 bbt_accent_modulo = 4;
1032                 break;
1033         case GridTypeBeatDiv20:
1034                 bbt_beat_subdivision = 5;
1035                 bbt_accent_modulo = 5;
1036                 break;
1037         case GridTypeBeatDiv24:
1038                 bbt_beat_subdivision = 6;
1039                 bbt_accent_modulo = 6;
1040                 break;
1041         case GridTypeBeatDiv28:
1042                 bbt_beat_subdivision = 7;
1043                 bbt_accent_modulo = 7;
1044                 break;
1045         case GridTypeBeatDiv32:
1046                 bbt_beat_subdivision = 4;
1047                 bbt_accent_modulo = 8;
1048                 break;
1049         case GridTypeBar:
1050         case GridTypeBeat:
1051                 bbt_beat_subdivision = 4;
1052                 break;
1053         case GridTypeNone:
1054         case GridTypeTimecode:
1055         case GridTypeMinSec:
1056         case GridTypeCDFrame:
1057                 bbt_beat_subdivision = 4;
1058                 break;
1059         }
1060
1061         const double ceil_upper_beat = floor (max (0.0, _session->tempo_map().beat_at_sample (upper))) + 1.0;
1062         if (ceil_upper_beat == floor_lower_beat) {
1063                 return;
1064         }
1065
1066         bbt_bars = _session->tempo_map().bbt_at_beat (ceil_upper_beat).bars - _session->tempo_map().bbt_at_beat (floor_lower_beat).bars;
1067
1068         beats = (ceil_upper_beat - floor_lower_beat);// - bbt_bars;  possible thinko; this fixes the problem (for me) where measure lines alternately appear&disappear while playing at certain zoom scales
1069         double beat_density = ((beats + 1) * ((double) (upper - lower) / (double) (1 + beat_after_upper_pos - beat_before_lower_pos))) / 5.0;
1070
1071         /* Only show the bar helper if there aren't many bars on the screen */
1072         if ((bbt_bars < 2) || (beats < 5)) {
1073                 bbt_bar_helper_on = true;
1074         }
1075
1076         //set upper limits on the beat_density based on the user's grid selection
1077         if (_grid_type == GridTypeBar) {
1078                 beat_density = fmax (beat_density, 16.01);
1079         } else if (_grid_type == GridTypeBeat) {
1080                 beat_density = fmax (beat_density, 4.01);
1081         }  else if (_grid_type == GridTypeBeatDiv2) {
1082                 beat_density = fmax (beat_density, 2.01);
1083         }  else if (_grid_type == GridTypeBeatDiv4) {
1084                 beat_density = fmax (beat_density, 1.001);
1085         } else if (_grid_type == GridTypeBeatDiv8) {
1086                 beat_density = fmax (beat_density, 0.501);
1087         } else if (_grid_type == GridTypeBeatDiv16) {
1088                 beat_density = fmax (beat_density, 0.2501);
1089         } else if (_grid_type == GridTypeBeatDiv32) {
1090                 beat_density = fmax (beat_density, 0.12501);
1091         }
1092
1093         if (beat_density > 2048) {
1094                 bbt_ruler_scale = bbt_show_many;
1095         } else if (beat_density > 512) {
1096                 bbt_ruler_scale = bbt_show_64;
1097         } else if (beat_density > 256) {
1098                 bbt_ruler_scale = bbt_show_16;
1099         } else if (beat_density > 64) {
1100                 bbt_ruler_scale = bbt_show_4;
1101         } else if (beat_density > 16) {
1102                 bbt_ruler_scale = bbt_show_1;
1103         } else if (beat_density > 4) {
1104                 bbt_ruler_scale =  bbt_show_quarters;
1105         } else  if (beat_density > 2) {
1106                 bbt_ruler_scale =  bbt_show_eighths;
1107         } else  if (beat_density > 1) {
1108                 bbt_ruler_scale =  bbt_show_sixteenths;
1109         } else {
1110                 bbt_ruler_scale =  bbt_show_thirtyseconds;
1111         }
1112 }
1113
1114 static void
1115 edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
1116 {
1117         ArdourCanvas::Ruler::Mark copy = marks.back();
1118         copy.label = newlabel;
1119         marks.pop_back ();
1120         marks.push_back (copy);
1121 }
1122
1123 void
1124 Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1125 {
1126         if (_session == 0) {
1127                 return;
1128         }
1129
1130         std::vector<TempoMap::BBTPoint>::const_iterator i;
1131
1132         char buf[64];
1133         gint  n = 0;
1134         samplepos_t pos;
1135         Timecode::BBT_Time next_beat;
1136         uint32_t beats = 0;
1137         uint32_t tick = 0;
1138         uint32_t skip;
1139         uint32_t t;
1140         double bbt_position_of_helper;
1141         bool i_am_accented = false;
1142         bool helper_active = false;
1143         ArdourCanvas::Ruler::Mark mark;
1144
1145         std::vector<TempoMap::BBTPoint> grid;
1146
1147         compute_current_bbt_points (grid, lower, upper);
1148
1149         if (distance (grid.begin(), grid.end()) == 0) {
1150                 return;
1151         }
1152
1153         switch (bbt_ruler_scale) {
1154
1155         case bbt_show_quarters:
1156
1157                 beats = distance (grid.begin(), grid.end());
1158                 bbt_nmarks = beats + 2;
1159
1160                 mark.label = "";
1161                 mark.position = lower;
1162                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1163                 marks.push_back (mark);
1164
1165                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1166
1167                         if ((*i).sample < lower && (bbt_bar_helper_on)) {
1168                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1169                                 edit_last_mark_label (marks, buf);
1170                         } else {
1171
1172                                 if ((*i).is_bar()) {
1173                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1174                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1175                                 } else if (((*i).beat % 2 == 1)) {
1176                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1177                                         buf[0] = '\0';
1178                                 } else {
1179                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1180                                         buf[0] = '\0';
1181                                 }
1182                                 mark.label = buf;
1183                                 mark.position = (*i).sample;
1184                                 marks.push_back (mark);
1185                                 n++;
1186                         }
1187                 }
1188                 break;
1189
1190         case bbt_show_eighths:
1191
1192                 beats = distance (grid.begin(), grid.end());
1193                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1194
1195                 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1196
1197                 // could do marks.assign() here to preallocate
1198
1199                 mark.label = "";
1200                 mark.position = lower;
1201                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1202                 marks.push_back (mark);
1203
1204                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1205
1206                         if ((*i).sample < lower && (bbt_bar_helper_on)) {
1207                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1208                                 edit_last_mark_label (marks, buf);
1209                                 helper_active = true;
1210                         } else {
1211
1212                                 if ((*i).is_bar()) {
1213                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1214                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1215                                 } else {
1216                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1217                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1218                                 }
1219                                 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1220                                         buf[0] = '\0';
1221                                 }
1222                                 mark.label =  buf;
1223                                 mark.position = (*i).sample;
1224                                 marks.push_back (mark);
1225                                 n++;
1226                         }
1227
1228                         /* Add the tick marks */
1229                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1230                         tick = skip; // the first non-beat tick
1231                         t = 0;
1232                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1233
1234                                 next_beat.beats = (*i).beat;
1235                                 next_beat.bars = (*i).bar;
1236                                 next_beat.ticks = tick;
1237                                 pos = _session->tempo_map().sample_at_bbt (next_beat);
1238
1239                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1240                                         i_am_accented = true;
1241                                 }
1242                                 mark.label = "";
1243                                 mark.position = pos;
1244
1245                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1246                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1247                                 } else {
1248                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1249                                 }
1250                                 i_am_accented = false;
1251                                 marks.push_back (mark);
1252
1253                                 tick += skip;
1254                                 ++t;
1255                                 ++n;
1256                         }
1257                 }
1258
1259           break;
1260
1261         case bbt_show_sixteenths:
1262
1263                 beats = distance (grid.begin(), grid.end());
1264                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1265
1266                 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1267
1268                 mark.label = "";
1269                 mark.position = lower;
1270                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1271                 marks.push_back (mark);
1272
1273                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1274
1275                         if ((*i).sample < lower && (bbt_bar_helper_on)) {
1276                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1277                                 edit_last_mark_label (marks, buf);
1278                                 helper_active = true;
1279                         } else {
1280
1281                                 if ((*i).is_bar()) {
1282                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1283                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1284                                 } else {
1285                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1286                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1287                                 }
1288                                 if (((*i).sample < bbt_position_of_helper) && helper_active) {
1289                                         buf[0] = '\0';
1290                                 }
1291                                 mark.label =  buf;
1292                                 mark.position = (*i).sample;
1293                                 marks.push_back (mark);
1294                                 n++;
1295                         }
1296
1297                         /* Add the tick marks */
1298                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1299                         tick = skip; // the first non-beat tick
1300
1301                         t = 0;
1302                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1303
1304                                 next_beat.beats = (*i).beat;
1305                                 next_beat.bars = (*i).bar;
1306                                 next_beat.ticks = tick;
1307                                 pos = _session->tempo_map().sample_at_bbt (next_beat);
1308
1309                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1310                                         i_am_accented = true;
1311                                 }
1312                                 if (i_am_accented && (pos > bbt_position_of_helper)){
1313                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1314                                 } else {
1315                                         buf[0] = '\0';
1316                                 }
1317
1318                                 mark.label = buf;
1319                                 mark.position = pos;
1320
1321                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1322                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1323                                 } else {
1324                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1325                                 }
1326                                 i_am_accented = false;
1327                                 marks.push_back (mark);
1328
1329                                 tick += skip;
1330                                 ++t;
1331                                 ++n;
1332                         }
1333                 }
1334
1335           break;
1336
1337         case bbt_show_thirtyseconds:
1338
1339                 beats = distance (grid.begin(), grid.end());
1340                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1341
1342                 bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
1343
1344                 mark.label = "";
1345                 mark.position = lower;
1346                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1347                 marks.push_back (mark);
1348
1349                 for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
1350
1351                         if ((*i).sample < lower && (bbt_bar_helper_on)) {
1352                                   snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1353                                   edit_last_mark_label (marks, buf);
1354                                   helper_active = true;
1355                         } else {
1356
1357                                   if ((*i).is_bar()) {
1358                                           mark.style = ArdourCanvas::Ruler::Mark::Major;
1359                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1360                                   } else {
1361                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1362                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1363                                   }
1364                                   if (((*i).sample < bbt_position_of_helper) && helper_active) {
1365                                           buf[0] = '\0';
1366                                   }
1367                                   mark.label =  buf;
1368                                   mark.position = (*i).sample;
1369                                   marks.push_back (mark);
1370                                   n++;
1371                         }
1372
1373                         /* Add the tick marks */
1374                         skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
1375
1376                         next_beat.beats = (*i).beat;
1377                         next_beat.bars = (*i).bar;
1378                         tick = skip; // the first non-beat tick
1379                         t = 0;
1380                         while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
1381
1382                                 next_beat.ticks = tick;
1383                                 pos = _session->tempo_map().sample_at_bbt (next_beat);
1384                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1385                                         i_am_accented = true;
1386                                 }
1387
1388                                 if (pos > bbt_position_of_helper) {
1389                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1390                                 } else {
1391                                         buf[0] = '\0';
1392                                 }
1393
1394                                 mark.label = buf;
1395                                 mark.position = pos;
1396
1397                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1398                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1399                                 } else {
1400                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1401                                 }
1402                                 i_am_accented = false;
1403                                 marks.push_back (mark);
1404
1405                                 tick += skip;
1406                                 ++t;
1407                                 ++n;
1408                         }
1409                 }
1410
1411           break;
1412
1413         case bbt_show_many:
1414                 bbt_nmarks = 1;
1415                 snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars);
1416                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1417                 mark.label = buf;
1418                 mark.position = lower;
1419                 marks.push_back (mark);
1420                 break;
1421
1422         case bbt_show_64:
1423                         bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1424                         for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1425                                 if ((*i).is_bar()) {
1426                                         if ((*i).bar % 64 == 1) {
1427                                                 if ((*i).bar % 256 == 1) {
1428                                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1429                                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1430                                                 } else {
1431                                                         buf[0] = '\0';
1432                                                         if ((*i).bar % 256 == 129)  {
1433                                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1434                                                         } else {
1435                                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1436                                                         }
1437                                                 }
1438                                                 mark.label = buf;
1439                                                 mark.position = (*i).sample;
1440                                                 marks.push_back (mark);
1441                                                 ++n;
1442                                         }
1443                                 }
1444                         }
1445                         break;
1446
1447         case bbt_show_16:
1448                 bbt_nmarks = (bbt_bars / 16) + 1;
1449                 for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
1450                         if ((*i).is_bar()) {
1451                           if ((*i).bar % 16 == 1) {
1452                                 if ((*i).bar % 64 == 1) {
1453                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1454                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1455                                 } else {
1456                                         buf[0] = '\0';
1457                                         if ((*i).bar % 64 == 33)  {
1458                                                 mark.style = ArdourCanvas::Ruler::Mark::Minor;
1459                                         } else {
1460                                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1461                                         }
1462                                 }
1463                                 mark.label = buf;
1464                                 mark.position = (*i).sample;
1465                                 marks.push_back (mark);
1466                                 ++n;
1467                           }
1468                         }
1469                 }
1470           break;
1471
1472         case bbt_show_4:
1473                 bbt_nmarks = (bbt_bars / 4) + 1;
1474                 for (n = 0, i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1475                         if ((*i).is_bar()) {
1476                                 if ((*i).bar % 4 == 1) {
1477                                         if ((*i).bar % 16 == 1) {
1478                                                 snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1479                                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1480                                         } else {
1481                                                 buf[0] = '\0';
1482                                                 if ((*i).bar % 16 == 9)  {
1483                                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1484                                                 } else {
1485                                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
1486                                                 }
1487                                         }
1488                                         mark.label = buf;
1489                                         mark.position = (*i).sample;
1490                                         marks.push_back (mark);
1491                                         ++n;
1492                                 }
1493                         }
1494                 }
1495           break;
1496
1497         case bbt_show_1:
1498 //      default:
1499                 bbt_nmarks = bbt_bars + 2;
1500                 for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
1501                         if ((*i).is_bar()) {
1502                           if ((*i).bar % 4 == 1) {
1503                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1504                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1505                           } else {
1506                                   buf[0] = '\0';
1507                                   if ((*i).bar % 4 == 3)  {
1508                                           mark.style = ArdourCanvas::Ruler::Mark::Minor;
1509                                   } else {
1510                                           mark.style = ArdourCanvas::Ruler::Mark::Micro;
1511                                   }
1512                           }
1513                           mark.label = buf;
1514                           mark.position = (*i).sample;
1515                           marks.push_back (mark);
1516                           ++n;
1517                         }
1518                 }
1519                 break;
1520
1521         }
1522 }
1523
1524 void
1525 Editor::set_samples_ruler_scale (samplepos_t lower, samplepos_t upper)
1526 {
1527         _samples_ruler_interval = (upper - lower) / 5;
1528 }
1529
1530 void
1531 Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
1532 {
1533         samplepos_t pos;
1534         samplepos_t const ilower = (samplepos_t) floor (lower);
1535         gchar buf[16];
1536         gint nmarks;
1537         gint n;
1538         ArdourCanvas::Ruler::Mark mark;
1539
1540         if (_session == 0) {
1541                 return;
1542         }
1543
1544         nmarks = 5;
1545         for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
1546                 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1547                 mark.label = buf;
1548                 mark.position = pos;
1549                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1550                 marks.push_back (mark);
1551         }
1552 }
1553
1554 static void
1555 sample_to_clock_parts (samplepos_t sample,
1556                        samplepos_t sample_rate,
1557                        long*       hrs_p,
1558                        long*       mins_p,
1559                        long*       secs_p,
1560                        long*       millisecs_p)
1561 {
1562         samplepos_t left;
1563         long hrs;
1564         long mins;
1565         long secs;
1566         long millisecs;
1567
1568         left = sample;
1569         hrs = left / (sample_rate * 60 * 60 * 1000);
1570         left -= hrs * sample_rate * 60 * 60 * 1000;
1571         mins = left / (sample_rate * 60 * 1000);
1572         left -= mins * sample_rate * 60 * 1000;
1573         secs = left / (sample_rate * 1000);
1574         left -= secs * sample_rate * 1000;
1575         millisecs = left / sample_rate;
1576
1577         *millisecs_p = millisecs;
1578         *secs_p = secs;
1579         *mins_p = mins;
1580         *hrs_p = hrs;
1581
1582         return;
1583 }
1584
1585 void
1586 Editor::set_minsec_ruler_scale (samplepos_t lower, samplepos_t upper)
1587 {
1588         samplepos_t fr = _session->sample_rate() * 1000;
1589         samplepos_t spacer;
1590
1591         if (_session == 0) {
1592                 return;
1593         }
1594
1595
1596         /* to prevent 'flashing' */
1597         if (lower > (spacer = (samplepos_t)(128 * Editor::get_current_zoom ()))) {
1598                 lower -= spacer;
1599         } else {
1600                 lower = 0;
1601         }
1602         upper += spacer;
1603         samplecnt_t const range = (upper - lower) * 1000;
1604
1605         if (range <= (fr / 10)) { /* 0-0.1 second */
1606                 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1607                 minsec_ruler_scale = minsec_show_msecs;
1608                 minsec_mark_modulo = 10;
1609                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1610         } else if (range <= (fr / 2)) { /* 0-0.5 second */
1611                 minsec_mark_interval = fr / 100;  /* show 1/100 seconds */
1612                 minsec_ruler_scale = minsec_show_msecs;
1613                 minsec_mark_modulo = 100;
1614                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1615         } else if (range <= fr) { /* 0-1 second */
1616                 minsec_mark_interval = fr / 10;  /* show 1/10 seconds */
1617                 minsec_ruler_scale = minsec_show_msecs;
1618                 minsec_mark_modulo = 200;
1619                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1620         } else if (range <= 2 * fr) { /* 1-2 seconds */
1621                 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1622                 minsec_ruler_scale = minsec_show_msecs;
1623                 minsec_mark_modulo = 500;
1624                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1625         } else if (range <= 8 * fr) { /* 2-5 seconds */
1626                 minsec_mark_interval =  fr / 5; /* show 2 seconds */
1627                 minsec_ruler_scale = minsec_show_msecs;
1628                 minsec_mark_modulo = 1000;
1629                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1630         } else if (range <= 16 * fr) { /* 8-16 seconds */
1631                 minsec_mark_interval =  fr; /* show 1 seconds */
1632                 minsec_ruler_scale = minsec_show_seconds;
1633                 minsec_mark_modulo = 2;
1634                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1635         } else if (range <= 30 * fr) { /* 10-30 seconds */
1636                 minsec_mark_interval =  fr; /* show 1 seconds */
1637                 minsec_ruler_scale = minsec_show_seconds;
1638                 minsec_mark_modulo = 5;
1639                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1640         } else if (range <= 60 * fr) { /* 30-60 seconds */
1641                 minsec_mark_interval = fr; /* show 1 seconds */
1642                 minsec_ruler_scale = minsec_show_seconds;
1643                 minsec_mark_modulo = 5;
1644                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1645         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1646                 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1647                 minsec_ruler_scale = minsec_show_seconds;
1648                 minsec_mark_modulo = 3;
1649                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1650         } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1651                 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1652                 minsec_ruler_scale = minsec_show_seconds;
1653                 minsec_mark_modulo = 30;
1654                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1655         } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1656                 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1657                 minsec_ruler_scale = minsec_show_seconds;
1658                 minsec_mark_modulo = 120;
1659                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1660         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1661                 minsec_mark_interval =  60 * fr; /* show 1 minute */
1662                 minsec_ruler_scale = minsec_show_minutes;
1663                 minsec_mark_modulo = 5;
1664                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1665         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1666                 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1667                 minsec_ruler_scale = minsec_show_minutes;
1668                 minsec_mark_modulo = 10;
1669                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1670         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1671                 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1672                 minsec_ruler_scale = minsec_show_minutes;
1673                 minsec_mark_modulo = 30;
1674                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1675         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1676                 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1677                 minsec_ruler_scale = minsec_show_minutes;
1678                 minsec_mark_modulo = 60;
1679                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1680         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1681                 minsec_mark_interval =  60 * 60 * fr; /* show 60 minutes */
1682                 minsec_ruler_scale = minsec_show_hours;
1683                 minsec_mark_modulo = 2;
1684                 minsec_nmarks = 2 + (range / minsec_mark_interval);
1685         } else {
1686
1687                 const samplecnt_t hours_in_range = range / (60 * 60 * fr);
1688                 const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
1689
1690                 /* Normally we do not need to know anything about the width of the canvas
1691                    to set the ruler scale, because the caller has already determined
1692                    the width and set lower + upper arguments to this function to match that.
1693
1694                    But in this case, where the range defined by lower and uppper can vary
1695                    substantially (anything from 24hrs+ to several billion years)
1696                    trying to decide which tick marks to show does require us to know
1697                    about the available width.
1698                 */
1699
1700                 minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
1701                 minsec_mark_modulo = max ((samplecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
1702                 minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
1703                 minsec_ruler_scale = minsec_show_many_hours;
1704         }
1705 }
1706
1707 void
1708 Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
1709 {
1710         samplepos_t pos;
1711         samplepos_t spacer;
1712         long hrs, mins, secs, millisecs;
1713         gchar buf[16];
1714         gint n;
1715         ArdourCanvas::Ruler::Mark mark;
1716
1717         if (_session == 0) {
1718                 return;
1719         }
1720
1721         /* to prevent 'flashing' */
1722         if (lower > (spacer = (samplepos_t) (128 * Editor::get_current_zoom ()))) {
1723                 lower = lower - spacer;
1724         } else {
1725                 lower = 0;
1726         }
1727
1728         if (minsec_mark_interval == 0) {  //we got here too early; divide-by-zero imminent
1729                 return;
1730         }
1731
1732         pos = (((1000 * (samplepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1733
1734         switch (minsec_ruler_scale) {
1735
1736         case minsec_show_msecs:
1737                 for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
1738                         sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1739                         if (millisecs % minsec_mark_modulo == 0) {
1740                                 if (millisecs == 0) {
1741                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1742                                 } else {
1743                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1744                                 }
1745                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1746                         } else {
1747                                 buf[0] = '\0';
1748                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1749                         }
1750                         mark.label = buf;
1751                         mark.position = pos/1000.0;
1752                         marks.push_back (mark);
1753                 }
1754                 break;
1755
1756         case minsec_show_seconds:
1757                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1758                         sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1759                         if (secs % minsec_mark_modulo == 0) {
1760                                 if (secs == 0) {
1761                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1762                                 } else {
1763                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1764                                 }
1765                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1766                         } else {
1767                                 buf[0] = '\0';
1768                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1769                         }
1770                         mark.label = buf;
1771                         mark.position = pos/1000.0;
1772                         marks.push_back (mark);
1773                 }
1774                 break;
1775
1776         case minsec_show_minutes:
1777                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1778                         sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1779                         if (mins % minsec_mark_modulo == 0) {
1780                                 if (mins == 0) {
1781                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
1782                                 } else {
1783                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
1784                                 }
1785                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
1786                         } else {
1787                                 buf[0] = '\0';
1788                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1789                         }
1790                         mark.label = buf;
1791                         mark.position = pos/1000.0;
1792                         marks.push_back (mark);
1793                 }
1794                 break;
1795
1796         case minsec_show_hours:
1797                  for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1798                         sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1799                         if (hrs % minsec_mark_modulo == 0) {
1800                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1801                                 snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
1802                         } else {
1803                                 buf[0] = '\0';
1804                                 mark.style = ArdourCanvas::Ruler::Mark::Micro;
1805                         }
1806                         mark.label = buf;
1807                         mark.position = pos/1000.0;
1808                         marks.push_back (mark);
1809                  }
1810                  break;
1811
1812         case minsec_show_many_hours:
1813                 for (n = 0; n < minsec_nmarks;) {
1814                         sample_to_clock_parts (pos, _session->sample_rate(), &hrs, &mins, &secs, &millisecs);
1815                         if (hrs % minsec_mark_modulo == 0) {
1816                                 mark.style = ArdourCanvas::Ruler::Mark::Major;
1817                                 snprintf (buf, sizeof(buf), "%02ld:00", hrs);
1818                                 mark.label = buf;
1819                                 mark.position = pos/1000.0;
1820                                 marks.push_back (mark);
1821                                 ++n;
1822                         }
1823                         pos += minsec_mark_interval;
1824                 }
1825                 break;
1826         }
1827 }