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