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