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