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