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