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