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