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