most of the 2.X->3.0 commit (up to rev 4299) except for gtk2_ardour/editor_canvas...
[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         if (trackview_by_y_position(bottom_track_pos) != 0) {
847                 trackview_by_y_position(bottom_track_pos)->clip_to_viewport ();
848         }
849
850         ruler_label_vbox.set_size_request (-1, (int)(timebar_height * visible_rulers));
851         time_canvas_vbox.set_size_request (-1,-1);
852
853         update_fixed_rulers();
854         redisplay_tempo (false);
855 }
856
857 void
858 Editor::update_just_smpte ()
859 {
860         ENSURE_GUI_THREAD(mem_fun(*this, &Editor::update_just_smpte));
861         
862         if (session == 0) {
863                 return;
864         }
865
866         nframes64_t rightmost_frame = leftmost_frame + current_page_frames();
867
868         if (ruler_timecode_action->get_active()) {
869                 gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_smpte_ruler), leftmost_frame, rightmost_frame,
870                                             leftmost_frame, session->current_end_frame());
871         }
872 }
873
874 void
875 Editor::compute_fixed_ruler_scale ()
876 {
877         if (session == 0) {
878                 return;
879         }
880
881         if (ruler_timecode_action->get_active()) {
882                 set_smpte_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames() );
883         }
884         
885         if (ruler_minsec_action->get_active()) {
886                 set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames() );
887         }
888 }
889
890 void
891 Editor::update_fixed_rulers ()
892 {
893         nframes64_t rightmost_frame;
894
895         if (session == 0) {
896                 return;
897         }
898
899         ruler_metrics[ruler_metric_smpte].units_per_pixel = frames_per_unit;
900         ruler_metrics[ruler_metric_frames].units_per_pixel = frames_per_unit;
901         ruler_metrics[ruler_metric_minsec].units_per_pixel = frames_per_unit;
902
903         rightmost_frame = leftmost_frame + current_page_frames();
904
905         /* these force a redraw, which in turn will force execution of the metric callbacks
906            to compute the relevant ticks to display.
907         */
908
909         if (ruler_timecode_action->get_active()) {
910                 gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_smpte_ruler), leftmost_frame, rightmost_frame,
911                                             leftmost_frame, session->current_end_frame());
912         }
913         
914         if (ruler_samples_action->get_active()) {
915                 gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_frames_ruler), leftmost_frame, rightmost_frame,
916                                             leftmost_frame, session->current_end_frame());
917         }
918         
919         if (ruler_minsec_action->get_active()) {
920                 gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_minsec_ruler), leftmost_frame, rightmost_frame,
921                                             leftmost_frame, session->current_end_frame());
922         }
923 }               
924
925 void
926 Editor::update_tempo_based_rulers ()
927 {
928         if (session == 0) {
929                 return;
930         }
931
932         ruler_metrics[ruler_metric_bbt].units_per_pixel = frames_per_unit;
933         
934         if (ruler_bbt_action->get_active()) {
935                 gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_bbt_ruler), leftmost_frame, leftmost_frame+current_page_frames(),
936                                             leftmost_frame, session->current_end_frame());
937         }
938 }
939
940 /* Mark generation */
941
942 gint
943 Editor::_metric_get_smpte (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
944 {
945         return ruler_editor->metric_get_smpte (marks, lower, upper, maxchars);
946 }
947
948 gint
949 Editor::_metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
950 {
951         return ruler_editor->metric_get_bbt (marks, lower, upper, maxchars);
952 }
953
954 gint
955 Editor::_metric_get_frames (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
956 {
957         return ruler_editor->metric_get_frames (marks, lower, upper, maxchars);
958 }
959
960 gint
961 Editor::_metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
962 {
963         return ruler_editor->metric_get_minsec (marks, lower, upper, maxchars);
964 }
965
966 void
967 Editor::set_smpte_ruler_scale (gdouble lower, gdouble upper)
968 {
969         nframes64_t range;
970         nframes64_t spacer;
971         nframes64_t fr;
972
973         if (session == 0) {
974                 return;
975         }
976
977         fr = session->frame_rate();
978
979         if (lower > (spacer = (nframes64_t)(128 * Editor::get_current_zoom ()))) {
980                 lower = lower - spacer;
981         } else {
982                 lower = 0;
983         }
984         upper = upper + spacer;
985         range = (nframes64_t) floor (upper - lower);
986
987         if (range < (2 * session->frames_per_smpte_frame())) { /* 0 - 2 frames */
988                 smpte_ruler_scale = smpte_show_bits;
989                 smpte_mark_modulo = 20;
990                 smpte_nmarks = 2 + (2 * Config->get_subframes_per_frame());
991         } else if (range <= (fr / 4)) { /* 2 frames - 0.250 second */
992                 smpte_ruler_scale = smpte_show_frames;
993                 smpte_mark_modulo = 1;
994                 smpte_nmarks = 2 + (range / (nframes64_t)session->frames_per_smpte_frame());
995         } else if (range <= (fr / 2)) { /* 0.25-0.5 second */
996                 smpte_ruler_scale = smpte_show_frames;
997                 smpte_mark_modulo = 2;
998                 smpte_nmarks = 2 + (range / (nframes64_t)session->frames_per_smpte_frame());
999         } else if (range <= fr) { /* 0.5-1 second */
1000                 smpte_ruler_scale = smpte_show_frames;
1001                 smpte_mark_modulo = 5;
1002                 smpte_nmarks = 2 + (range / (nframes64_t)session->frames_per_smpte_frame());
1003         } else if (range <= 2 * fr) { /* 1-2 seconds */
1004                 smpte_ruler_scale = smpte_show_frames;
1005                 smpte_mark_modulo = 10;
1006                 smpte_nmarks = 2 + (range / (nframes64_t)session->frames_per_smpte_frame());
1007         } else if (range <= 8 * fr) { /* 2-8 seconds */
1008                 smpte_ruler_scale = smpte_show_seconds;
1009                 smpte_mark_modulo = 1;
1010                 smpte_nmarks = 2 + (range / fr);
1011         } else if (range <= 16 * fr) { /* 8-16 seconds */
1012                 smpte_ruler_scale = smpte_show_seconds;
1013                 smpte_mark_modulo = 2;
1014                 smpte_nmarks = 2 + (range / fr);
1015         } else if (range <= 30 * fr) { /* 16-30 seconds */
1016                 smpte_ruler_scale = smpte_show_seconds;
1017                 smpte_mark_modulo = 5;
1018                 smpte_nmarks = 2 + (range / fr);
1019         } else if (range <= 60 * fr) { /* 30-60 seconds */
1020                 smpte_ruler_scale = smpte_show_seconds;
1021                 smpte_mark_modulo = 5;
1022                 smpte_nmarks = 2 + (range / fr);
1023         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1024                 smpte_ruler_scale = smpte_show_seconds;
1025                 smpte_mark_modulo = 15;
1026                 smpte_nmarks = 2 + (range / fr);
1027         } else if (range <= 4 * 60 * fr) { /* 2-4 minutes */
1028                 smpte_ruler_scale = smpte_show_seconds;
1029                 smpte_mark_modulo = 30;
1030                 smpte_nmarks = 2 + (range / fr);
1031         } else if (range <= 10 * 60 * fr) { /* 4-10 minutes */
1032                 smpte_ruler_scale = smpte_show_minutes;
1033                 smpte_mark_modulo = 2;
1034                 smpte_nmarks = 2 + 10;
1035         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1036                 smpte_ruler_scale = smpte_show_minutes;
1037                 smpte_mark_modulo = 5;
1038                 smpte_nmarks = 2 + 30;
1039         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1040                 smpte_ruler_scale = smpte_show_minutes;
1041                 smpte_mark_modulo = 10;
1042                 smpte_nmarks = 2 + 60;
1043         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1044                 smpte_ruler_scale = smpte_show_minutes;
1045                 smpte_mark_modulo = 30;
1046                 smpte_nmarks = 2 + (60 * 4);
1047         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1048                 smpte_ruler_scale = smpte_show_hours;
1049                 smpte_mark_modulo = 1;
1050                 smpte_nmarks = 2 + 8;
1051         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1052                 smpte_ruler_scale = smpte_show_hours;
1053                 smpte_mark_modulo = 1;
1054                 smpte_nmarks = 2 + 24;
1055         } else {
1056     
1057                 /* not possible if nframes64_t is a 32 bit quantity */
1058     
1059                 smpte_ruler_scale = smpte_show_hours;
1060                 smpte_mark_modulo = 4;
1061                 smpte_nmarks = 2 + 24;
1062         }
1063   
1064 }
1065
1066 gint
1067 Editor::metric_get_smpte (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
1068 {
1069         nframes_t pos;
1070         nframes64_t spacer;
1071         SMPTE::Time smpte;
1072         gchar buf[16];
1073         gint n;
1074
1075         if (session == 0) {
1076                 return 0;
1077         }
1078
1079         if (lower > (spacer = (nframes64_t)(128 * Editor::get_current_zoom ()))) {
1080                 lower = lower - spacer;
1081         } else {
1082                 lower = 0;
1083         }
1084
1085         pos = (nframes_t) floor (lower);
1086         
1087         *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * smpte_nmarks);  
1088         switch (smpte_ruler_scale) {
1089         case smpte_show_bits:
1090
1091                 // Find smpte time of this sample (pos) with subframe accuracy
1092                 session->sample_to_smpte(pos, smpte, true /* use_offset */, true /* use_subframes */ );
1093     
1094                 for (n = 0; n < smpte_nmarks; n++) {
1095                         session->smpte_to_sample(smpte, pos, true /* use_offset */, true /* use_subframes */ );
1096                         if ((smpte.subframes % smpte_mark_modulo) == 0) {
1097                                 if (smpte.subframes == 0) {
1098                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1099                                         snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", smpte.negative ? "-" : "", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1100                                 } else {
1101                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1102                                         snprintf (buf, sizeof(buf), ".%02u", smpte.subframes);
1103                                 }
1104                         } else {
1105                                 snprintf (buf, sizeof(buf)," ");
1106                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
1107         
1108                         }
1109                         (*marks)[n].label = g_strdup (buf);
1110                         (*marks)[n].position = pos;
1111
1112                         // Increment subframes by one
1113                         SMPTE::increment_subframes( smpte );
1114                 }
1115           break;
1116         case smpte_show_seconds:
1117                 // Find smpte time of this sample (pos)
1118                 session->sample_to_smpte(pos, smpte, true /* use_offset */, false /* use_subframes */ );
1119                 // Go to next whole second down
1120                 SMPTE::seconds_floor( smpte );
1121
1122                 for (n = 0; n < smpte_nmarks; n++) {
1123                         session->smpte_to_sample(smpte, pos, true /* use_offset */, false /* use_subframes */ );
1124                         if ((smpte.seconds % smpte_mark_modulo) == 0) {
1125                                 if (smpte.seconds == 0) {
1126                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1127                                         (*marks)[n].position = pos;
1128                                 } else {
1129                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1130                                         (*marks)[n].position = pos;
1131                                 }
1132                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", smpte.negative ? "-" : "", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1133                         } else {
1134                                 snprintf (buf, sizeof(buf)," ");
1135                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
1136                                 (*marks)[n].position = pos;
1137         
1138                         }
1139                         (*marks)[n].label = g_strdup (buf);
1140                         SMPTE::increment_seconds( smpte );
1141                 }
1142           break;
1143         case smpte_show_minutes:
1144                 // Find smpte time of this sample (pos)
1145                 session->sample_to_smpte(pos, smpte, true /* use_offset */, false /* use_subframes */ );
1146                 // Go to next whole minute down
1147                 SMPTE::minutes_floor( smpte );
1148
1149                 for (n = 0; n < smpte_nmarks; n++) {
1150                         session->smpte_to_sample(smpte, pos, true /* use_offset */, false /* use_subframes */ );
1151                         if ((smpte.minutes % smpte_mark_modulo) == 0) {
1152                                 if (smpte.minutes == 0) {
1153                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1154                                 } else {
1155                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1156                                 }
1157                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", smpte.negative ? "-" : "", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1158                         } else {
1159                                 snprintf (buf, sizeof(buf)," ");
1160                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
1161         
1162                         }
1163                         (*marks)[n].label = g_strdup (buf);
1164                         (*marks)[n].position = pos;
1165                         SMPTE::increment_minutes( smpte );
1166                 }
1167
1168           break;
1169         case smpte_show_hours:
1170                 // Find smpte time of this sample (pos)
1171                 session->sample_to_smpte(pos, smpte, true /* use_offset */, false /* use_subframes */ );
1172                 // Go to next whole hour down
1173                 SMPTE::hours_floor( smpte );
1174
1175                 for (n = 0; n < smpte_nmarks; n++) {
1176                         session->smpte_to_sample(smpte, pos, true /* use_offset */, false /* use_subframes */ );
1177                         if ((smpte.hours % smpte_mark_modulo) == 0) {
1178                                 (*marks)[n].style = GtkCustomRulerMarkMajor;
1179                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", smpte.negative ? "-" : "", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1180                         } else {
1181                                 snprintf (buf, sizeof(buf)," ");
1182                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
1183         
1184                         }
1185                         (*marks)[n].label = g_strdup (buf);
1186                         (*marks)[n].position = pos;
1187
1188                         SMPTE::increment_hours( smpte );
1189                 }
1190           break;
1191         case smpte_show_frames:
1192                 // Find smpte time of this sample (pos)
1193                 session->sample_to_smpte(pos, smpte, true /* use_offset */, false /* use_subframes */ );
1194                 // Go to next whole frame down
1195                 SMPTE::frames_floor( smpte );
1196
1197                 for (n = 0; n < smpte_nmarks; n++) {
1198                         session->smpte_to_sample(smpte, pos, true /* use_offset */, false /* use_subframes */ );
1199                         if ((smpte.frames % smpte_mark_modulo) == 0)  {
1200                                 if (smpte.frames == 0) {
1201                                   (*marks)[n].style = GtkCustomRulerMarkMajor;
1202                                 } else {
1203                                   (*marks)[n].style = GtkCustomRulerMarkMinor;
1204                                 }
1205                                 (*marks)[n].position = pos;
1206                                 snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", smpte.negative ? "-" : "", smpte.hours, smpte.minutes, smpte.seconds, smpte.frames);
1207                         } else {
1208                                 snprintf (buf, sizeof(buf)," ");
1209                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
1210                                 (*marks)[n].position = pos;
1211         
1212                         }
1213                         (*marks)[n].label = g_strdup (buf);
1214                         SMPTE::increment( smpte );
1215                 }
1216
1217           break;
1218         }
1219   
1220         return smpte_nmarks;
1221 }
1222
1223
1224 void
1225 Editor::compute_bbt_ruler_scale (nframes64_t lower, nframes64_t upper)
1226 {
1227         if (session == 0) {
1228                 return;
1229         }
1230         TempoMap::BBTPointList::iterator i;
1231         BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
1232
1233         session->bbt_time((jack_nframes_t) lower, lower_beat);
1234         session->bbt_time((jack_nframes_t) upper, upper_beat);
1235         uint32_t beats = 0;
1236
1237         bbt_accent_modulo = 1;
1238         bbt_bar_helper_on = false;
1239         bbt_bars = 0;
1240         bbt_nmarks = 1;
1241
1242         bbt_ruler_scale =  bbt_over;
1243   
1244         switch (snap_type) {
1245         case SnapToAThirdBeat:
1246                 bbt_beat_subdivision = 3;
1247                 break;
1248         case SnapToAQuarterBeat:
1249                 bbt_beat_subdivision = 4;
1250                 break;
1251         case SnapToAEighthBeat:
1252                 bbt_beat_subdivision = 8;
1253                 bbt_accent_modulo = 2;
1254                 break;
1255         case SnapToASixteenthBeat:
1256                 bbt_beat_subdivision = 16;
1257                 bbt_accent_modulo = 4;
1258                 break;
1259         case SnapToAThirtysecondBeat:
1260                 bbt_beat_subdivision = 32;
1261                 bbt_accent_modulo = 8;
1262                 break;
1263         default:
1264                 bbt_beat_subdivision = 4;
1265                 break;
1266         }
1267
1268         if (current_bbt_points == 0 || current_bbt_points->empty()) {
1269                 return;
1270         }
1271
1272         i = current_bbt_points->end();
1273         i--;
1274         if ((*i).beat >= (*current_bbt_points->begin()).beat) {
1275           bbt_bars = (*i).bar - (*current_bbt_points->begin()).bar;
1276         } else {
1277           bbt_bars = (*i).bar - (*current_bbt_points->begin()).bar - 1;
1278         }
1279         beats = current_bbt_points->size() - bbt_bars;
1280
1281         /*Only show the bar helper if there aren't many bars on the screen */
1282         if ((bbt_bars < 2) || (beats < 5)) {
1283                 bbt_bar_helper_on = true;
1284         }
1285
1286         if (bbt_bars > 8192) {
1287           bbt_ruler_scale =  bbt_over;
1288         } else if (bbt_bars > 1024) {
1289           bbt_ruler_scale = bbt_show_64;
1290         } else if (bbt_bars > 256) {
1291           bbt_ruler_scale = bbt_show_16;
1292         } else if (bbt_bars > 64) {
1293           bbt_ruler_scale = bbt_show_4;
1294         } else if (bbt_bars > 10) {
1295           bbt_ruler_scale =  bbt_show_1;
1296         } else if (bbt_bars > 2) {
1297           bbt_ruler_scale =  bbt_show_beats;
1298         } else  if (bbt_bars > 0) {
1299           bbt_ruler_scale =  bbt_show_ticks;
1300         } else {
1301           bbt_ruler_scale =  bbt_show_ticks_detail;
1302         } 
1303
1304         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)) {
1305                 bbt_ruler_scale =  bbt_show_ticks_super_detail;
1306         }
1307 }
1308
1309 gint
1310 Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
1311 {
1312         if (session == 0) {
1313                 return 0;
1314         }
1315
1316         TempoMap::BBTPointList::iterator i;
1317
1318         char buf[64];
1319         gint  n = 0;
1320         nframes64_t pos;
1321         BBT_Time next_beat;
1322         nframes64_t next_beat_pos;
1323         uint32_t beats = 0;
1324
1325         uint32_t tick = 0;
1326         uint32_t skip;
1327         uint32_t t;
1328         nframes64_t frame_skip;
1329         double frame_skip_error;
1330         double bbt_position_of_helper;
1331         double accumulated_error;
1332         bool i_am_accented = false;
1333         bool helper_active = false;
1334
1335         if (current_bbt_points == 0 || current_bbt_points->empty()) {
1336                 return 0;
1337         }
1338
1339         switch (bbt_ruler_scale) {
1340
1341         case bbt_show_beats:
1342                 beats = current_bbt_points->size();
1343                 bbt_nmarks = beats + 2;
1344
1345                 *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
1346
1347                 (*marks)[0].label = g_strdup(" ");
1348                 (*marks)[0].position = lower;
1349                 (*marks)[0].style = GtkCustomRulerMarkMicro;
1350                 
1351                 for (n = 1,   i = current_bbt_points->begin(); n < bbt_nmarks && i != current_bbt_points->end(); ++i) {
1352                         if  ((*i).type != TempoMap::Beat) {
1353                                 continue;
1354                         }
1355                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1356                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1357                                 (*marks)[0].label = g_strdup (buf); 
1358                                 helper_active = true;
1359                         } else {
1360
1361                                 if ((*i).beat == 1) {
1362                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1363                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1364                                 } else if (((*i).beat % 2 == 1)) {
1365                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1366                                         snprintf (buf, sizeof(buf), " ");
1367                                 } else {
1368                                         (*marks)[n].style = GtkCustomRulerMarkMicro;
1369                                         snprintf (buf, sizeof(buf), " ");
1370                                 }
1371                                 (*marks)[n].label =  g_strdup (buf);
1372                                 (*marks)[n].position = (*i).frame;
1373                                 n++;
1374                         }
1375                 }
1376                 break;
1377
1378         case bbt_show_ticks:
1379
1380                 beats = current_bbt_points->size();
1381                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1382
1383                 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1384                 *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
1385
1386                 (*marks)[0].label = g_strdup(" ");
1387                 (*marks)[0].position = lower;
1388                 (*marks)[0].style = GtkCustomRulerMarkMicro;
1389                 
1390                 for (n = 1,   i = current_bbt_points->begin(); n < bbt_nmarks && i != current_bbt_points->end(); ++i) {
1391                         if  ((*i).type != TempoMap::Beat) {
1392                                 continue;
1393                         }
1394                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1395                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1396                                 (*marks)[0].label = g_strdup (buf); 
1397                                 helper_active = true;
1398                         } else {
1399
1400                                 if ((*i).beat == 1) {
1401                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1402                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1403                                 } else {
1404                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1405                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1406                                 }
1407                                 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1408                                         snprintf (buf, sizeof(buf), " ");
1409                                 }
1410                                 (*marks)[n].label =  g_strdup (buf);
1411                                 (*marks)[n].position = (*i).frame;
1412                                 n++;
1413                         }
1414                         
1415                         /* Add the tick marks */
1416
1417                         /* Find the next beat */
1418                         next_beat.beats = (*i).beat;
1419                         next_beat.bars = (*i).bar;
1420                         next_beat.ticks = 0;
1421                         
1422                         if ((*i).meter->beats_per_bar() > (next_beat.beats + 1)) {
1423                                   next_beat.beats += 1;
1424                         } else {
1425                                   next_beat.bars += 1;
1426                                   next_beat.beats = 1;
1427                         }
1428                                 
1429                         next_beat_pos = session->tempo_map().frame_time(next_beat);
1430                         
1431                         frame_skip = (nframes64_t) floor (frame_skip_error = (session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1432                         frame_skip_error -= frame_skip;
1433                         skip = (uint32_t) (Meter::ticks_per_beat / bbt_beat_subdivision);
1434
1435                         pos = (*i).frame + frame_skip;
1436                         accumulated_error = frame_skip_error;
1437
1438                         tick = skip;
1439                         
1440                         for (t = 0; (tick < Meter::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1441
1442                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1443                                         i_am_accented = true;
1444                                 }
1445
1446                                 snprintf (buf, sizeof(buf), " ");
1447                                 (*marks)[n].label = g_strdup (buf);
1448
1449                                 /* Error compensation for float to nframes64_t*/
1450                                 accumulated_error += frame_skip_error;
1451                                 if (accumulated_error > 1) {
1452                                         pos += 1;
1453                                         accumulated_error -= 1.0f;
1454                                 }
1455
1456                                 (*marks)[n].position = pos;
1457
1458                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1459                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1460                                 } else {
1461                                         (*marks)[n].style = GtkCustomRulerMarkMicro;
1462                                 }
1463                                 i_am_accented = false;
1464                                 n++;
1465                         }
1466                 }
1467
1468           break;
1469
1470         case bbt_show_ticks_detail:
1471
1472                 beats = current_bbt_points->size();
1473                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1474
1475                 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1476                 *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
1477
1478                 (*marks)[0].label = g_strdup(" ");
1479                 (*marks)[0].position = lower;
1480                 (*marks)[0].style = GtkCustomRulerMarkMicro;
1481                 
1482                 for (n = 1,   i = current_bbt_points->begin(); n < bbt_nmarks && i != current_bbt_points->end(); ++i) {
1483                         if  ((*i).type != TempoMap::Beat) {
1484                                 continue;
1485                         }
1486                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1487                                 snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1488                                 (*marks)[0].label = g_strdup (buf); 
1489                                 helper_active = true;
1490                         } else {
1491
1492                                 if ((*i).beat == 1) {
1493                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1494                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1495                                 } else {
1496                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1497                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1498                                 }
1499                                 if (((*i).frame < bbt_position_of_helper) && helper_active) {
1500                                         snprintf (buf, sizeof(buf), " ");
1501                                 }
1502                                 (*marks)[n].label =  g_strdup (buf);
1503                                 (*marks)[n].position = (*i).frame;
1504                                 n++;
1505                         }
1506                         
1507                         /* Add the tick marks */
1508
1509                         /* Find the next beat */
1510
1511                         next_beat.beats = (*i).beat;
1512                         next_beat.bars = (*i).bar;
1513                         
1514                         if ((*i).meter->beats_per_bar() > (next_beat.beats + 1)) {
1515                                   next_beat.beats += 1;
1516                         } else {
1517                                   next_beat.bars += 1;
1518                                   next_beat.beats = 1;
1519                         }
1520                                 
1521                         next_beat_pos = session->tempo_map().frame_time(next_beat);
1522                         
1523                         frame_skip = (nframes64_t) floor (frame_skip_error = (session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1524                         frame_skip_error -= frame_skip;
1525                         skip = (uint32_t) (Meter::ticks_per_beat / bbt_beat_subdivision);
1526
1527                         pos = (*i).frame + frame_skip;
1528                         accumulated_error = frame_skip_error;
1529
1530                         tick = skip;
1531                         
1532                         for (t = 0; (tick < Meter::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1533
1534                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1535                                         i_am_accented = true;
1536                                 }
1537
1538                                 if (i_am_accented && (pos > bbt_position_of_helper)){
1539                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1540                                 } else {
1541                                         snprintf (buf, sizeof(buf), " ");
1542                                 }
1543
1544                                 (*marks)[n].label = g_strdup (buf);
1545
1546                                 /* Error compensation for float to nframes64_t*/
1547                                 accumulated_error += frame_skip_error;
1548                                 if (accumulated_error > 1) {
1549                                         pos += 1;
1550                                         accumulated_error -= 1.0f;
1551                                 }
1552
1553                                 (*marks)[n].position = pos;
1554
1555                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
1556                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1557                                 } else {
1558                                         (*marks)[n].style = GtkCustomRulerMarkMicro;
1559                                 }
1560                                 i_am_accented = false;
1561                                 n++;    
1562                         }
1563                 }
1564
1565           break;
1566
1567         case bbt_show_ticks_super_detail:
1568
1569                 beats = current_bbt_points->size();
1570                 bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
1571
1572                 bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
1573                 *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
1574
1575                 (*marks)[0].label = g_strdup(" ");
1576                 (*marks)[0].position = lower;
1577                 (*marks)[0].style = GtkCustomRulerMarkMicro;
1578                 
1579                 for (n = 1,   i = current_bbt_points->begin(); n < bbt_nmarks && i != current_bbt_points->end(); ++i) {
1580                         if  ((*i).type != TempoMap::Beat) {
1581                                   continue;
1582                         }
1583                         if ((*i).frame < lower && (bbt_bar_helper_on)) {
1584                                   snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
1585                                   (*marks)[0].label = g_strdup (buf); 
1586                                   helper_active = true;
1587                         } else {
1588
1589                                   if ((*i).beat == 1) {
1590                                           (*marks)[n].style = GtkCustomRulerMarkMajor;
1591                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1592                                   } else {
1593                                           (*marks)[n].style = GtkCustomRulerMarkMinor;
1594                                           snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
1595                                   }
1596                                   if (((*i).frame < bbt_position_of_helper) && helper_active) {
1597                                           snprintf (buf, sizeof(buf), " ");
1598                                   }
1599                                   (*marks)[n].label =  g_strdup (buf);
1600                                   (*marks)[n].position = (*i).frame;
1601                                   n++;
1602                         }
1603                         
1604                         /* Add the tick marks */
1605
1606                         /* Find the next beat */
1607
1608                         next_beat.beats = (*i).beat;
1609                         next_beat.bars = (*i).bar;
1610                         
1611                         if ((*i).meter->beats_per_bar() > (next_beat.beats + 1)) {
1612                                   next_beat.beats += 1;
1613                         } else {
1614                                   next_beat.bars += 1;
1615                                   next_beat.beats = 1;
1616                         }
1617                                 
1618                         next_beat_pos = session->tempo_map().frame_time(next_beat);
1619                         
1620                         frame_skip = (nframes64_t) floor (frame_skip_error = (session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
1621                         frame_skip_error -= frame_skip;
1622                         skip = (uint32_t) (Meter::ticks_per_beat / bbt_beat_subdivision);
1623
1624                         pos = (*i).frame + frame_skip;
1625                         accumulated_error = frame_skip_error;
1626
1627                         tick = skip;
1628                         
1629                         for (t = 0; (tick < Meter::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
1630
1631                                   if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
1632                                           i_am_accented = true;
1633                                   }
1634
1635                                   if (pos > bbt_position_of_helper) {
1636                                           snprintf (buf, sizeof(buf), "%" PRIu32, tick);
1637                                   } else {
1638                                           snprintf (buf, sizeof(buf), " ");
1639                                   }
1640
1641                                   (*marks)[n].label = g_strdup (buf);
1642
1643                                   /* Error compensation for float to nframes64_t*/
1644                                   accumulated_error += frame_skip_error;
1645                                   if (accumulated_error > 1) {
1646                                           pos += 1;
1647                                           accumulated_error -= 1.0f;
1648                                   }
1649
1650                                   (*marks)[n].position = pos;
1651                                   
1652                                   if ((bbt_beat_subdivision > 4) && i_am_accented) {
1653                                           (*marks)[n].style = GtkCustomRulerMarkMinor;
1654                                   } else {
1655                                           (*marks)[n].style = GtkCustomRulerMarkMicro;
1656                                   }
1657                                   i_am_accented = false;
1658                                   n++;  
1659                         }
1660                 }
1661
1662           break;
1663
1664         case bbt_over:
1665                         bbt_nmarks = 1;
1666                         *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
1667                         snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
1668                         (*marks)[0].style = GtkCustomRulerMarkMajor;
1669                         (*marks)[0].label = g_strdup (buf);
1670                         (*marks)[0].position = lower;
1671                         n = 1;
1672
1673           break;
1674
1675         case bbt_show_64:
1676                         bbt_nmarks = (gint) (bbt_bars / 64) + 1;
1677                         *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
1678                         for (n = 0,   i = current_bbt_points->begin(); i != current_bbt_points->end() && n < bbt_nmarks; i++) {
1679                                 if ((*i).type == TempoMap::Bar)  {
1680                                         if ((*i).bar % 64 == 1) {
1681                                                 if ((*i).bar % 256 == 1) {
1682                                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1683                                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1684                                                 } else {
1685                                                         snprintf (buf, sizeof(buf), " ");
1686                                                         if ((*i).bar % 256 == 129)  {
1687                                                                 (*marks)[n].style = GtkCustomRulerMarkMinor;
1688                                                         } else {
1689                                                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
1690                                                         }
1691                                                 }
1692                                                 (*marks)[n].label = g_strdup (buf);
1693                                                 (*marks)[n].position = (*i).frame;
1694                                                 n++;
1695                                         }
1696                                 }
1697                         }
1698                         break;
1699
1700         case bbt_show_16:
1701                 bbt_nmarks = (bbt_bars / 16) + 1;
1702                 *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
1703                 for (n = 0,  i = current_bbt_points->begin(); i != current_bbt_points->end() && n < bbt_nmarks; i++) {
1704                         if ((*i).type == TempoMap::Bar)  {
1705                           if ((*i).bar % 16 == 1) {
1706                                 if ((*i).bar % 64 == 1) {
1707                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1708                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1709                                 } else {
1710                                         snprintf (buf, sizeof(buf), " ");
1711                                         if ((*i).bar % 64 == 33)  {
1712                                                 (*marks)[n].style = GtkCustomRulerMarkMinor;
1713                                         } else {
1714                                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
1715                                         }
1716                                 }
1717                                 (*marks)[n].label = g_strdup (buf);
1718                                 (*marks)[n].position = (*i).frame;
1719                                 n++;
1720                           }
1721                         }
1722                 }
1723           break;
1724
1725         case bbt_show_4:
1726                 bbt_nmarks = (bbt_bars / 4) + 1;
1727                 *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
1728                 for (n = 0,   i = current_bbt_points->begin(); i != current_bbt_points->end() && n < bbt_nmarks; ++i) {
1729                         if ((*i).type == TempoMap::Bar)  {
1730                           if ((*i).bar % 4 == 1) {
1731                                 if ((*i).bar % 16 == 1) {
1732                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1733                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1734                                 } else {
1735                                         snprintf (buf, sizeof(buf), " ");
1736                                         if ((*i).bar % 16 == 9)  {
1737                                                 (*marks)[n].style = GtkCustomRulerMarkMinor;
1738                                         } else {
1739                                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
1740                                         }
1741                                 }
1742                                 (*marks)[n].label = g_strdup (buf);
1743                                 (*marks)[n].position = (*i).frame;
1744                                 n++;
1745                           }
1746                         }
1747                 }
1748           break;
1749
1750         case bbt_show_1:
1751   //    default:
1752                 bbt_nmarks = bbt_bars + 2;
1753                 *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks );
1754                 for (n = 0,  i = current_bbt_points->begin(); i != current_bbt_points->end() && n < bbt_nmarks; i++) {
1755                         if ((*i).type == TempoMap::Bar)  {
1756                           if ((*i).bar % 4 == 1) {
1757                                         snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
1758                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1759                           } else {
1760                                 snprintf (buf, sizeof(buf), " ");
1761                                 if ((*i).bar % 4 == 3)  {
1762                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1763                                 } else {
1764                                         (*marks)[n].style = GtkCustomRulerMarkMicro;
1765                                 }
1766                           }
1767                         (*marks)[n].label = g_strdup (buf);
1768                         (*marks)[n].position = (*i).frame;
1769                         n++;
1770                         }
1771                 }
1772  
1773         break;
1774
1775         }
1776
1777         return n; //return the actual number of marks made, since we might have skipped some from fractional time signatures 
1778
1779 }
1780
1781 gint
1782 Editor::metric_get_frames (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
1783 {
1784         nframes64_t mark_interval;
1785         nframes64_t pos;
1786         nframes64_t ilower = (nframes64_t) floor (lower);
1787         nframes64_t iupper = (nframes64_t) floor (upper);
1788         gchar buf[16];
1789         gint nmarks;
1790         gint n;
1791
1792         if (session == 0) {
1793                 return 0;
1794         }
1795
1796         mark_interval = (iupper - ilower) / 5;
1797         if (mark_interval > session->frame_rate()) {
1798                 mark_interval -= mark_interval % session->frame_rate();
1799         } else {
1800                 mark_interval = session->frame_rate() / (session->frame_rate() / mark_interval ) ;
1801         }
1802         nmarks = 5;
1803         *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * nmarks);
1804         for (n = 0, pos = ilower; n < nmarks; pos += mark_interval, ++n) {
1805                 snprintf (buf, sizeof(buf), "%" PRIi64, pos);
1806                 (*marks)[n].label = g_strdup (buf);
1807                 (*marks)[n].position = pos;
1808                 (*marks)[n].style = GtkCustomRulerMarkMajor;
1809         }
1810         
1811         return nmarks;
1812 }
1813
1814 static void
1815 sample_to_clock_parts ( nframes64_t sample,
1816                         nframes64_t sample_rate, 
1817                         long *hrs_p,
1818                         long *mins_p,
1819                         long *secs_p,
1820                         long *millisecs_p)
1821
1822 {
1823         nframes64_t left;
1824         long hrs;
1825         long mins;
1826         long secs;
1827         long millisecs;
1828         
1829         left = sample;
1830         hrs = left / (sample_rate * 60 * 60);
1831         left -= hrs * sample_rate * 60 * 60;
1832         mins = left / (sample_rate * 60);
1833         left -= mins * sample_rate * 60;
1834         secs = left / sample_rate;
1835         left -= secs * sample_rate;
1836         millisecs = left * 1000 / sample_rate;
1837
1838         *millisecs_p = millisecs;
1839         *secs_p = secs;
1840         *mins_p = mins;
1841         *hrs_p = hrs;
1842
1843         return;
1844 }
1845
1846 void
1847 Editor::set_minsec_ruler_scale (gdouble lower, gdouble upper)
1848 {
1849         nframes64_t range;
1850         nframes64_t fr;
1851         nframes64_t spacer;
1852
1853         if (session == 0) {
1854                 return;
1855         }
1856
1857         fr = session->frame_rate();
1858
1859         /* to prevent 'flashing' */
1860         if (lower > (spacer = (nframes64_t)(128 * Editor::get_current_zoom ()))) {
1861                 lower -= spacer;
1862         } else {
1863                 lower = 0;
1864         }
1865         upper += spacer;
1866         range = (nframes64_t) (upper - lower);
1867
1868         if (range <  (fr / 50)) {
1869                 minsec_mark_interval =  fr / 1000; /* show 1/1000 seconds */
1870                 minsec_ruler_scale = minsec_show_frames;
1871                 minsec_mark_modulo = 10;
1872         } else if (range <= (fr / 10)) { /* 0-0.1 second */
1873                 minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
1874                 minsec_ruler_scale = minsec_show_frames;
1875                 minsec_mark_modulo = 10;
1876         } else if (range <= (fr / 2)) { /* 0-0.5 second */
1877                 minsec_mark_interval = fr / 100;  /* show 1/100 seconds */
1878                 minsec_ruler_scale = minsec_show_frames;
1879                 minsec_mark_modulo = 100;
1880         } else if (range <= fr) { /* 0-1 second */
1881                 minsec_mark_interval = fr / 10;  /* show 1/10 seconds */
1882                 minsec_ruler_scale = minsec_show_frames;
1883                 minsec_mark_modulo = 200;
1884         } else if (range <= 2 * fr) { /* 1-2 seconds */
1885                 minsec_mark_interval = fr / 10; /* show 1/10 seconds */
1886                 minsec_ruler_scale = minsec_show_frames;
1887                 minsec_mark_modulo = 500;
1888         } else if (range <= 8 * fr) { /* 2-5 seconds */
1889                 minsec_mark_interval =  fr / 5; /* show 2 seconds */
1890                 minsec_ruler_scale = minsec_show_frames;
1891                 minsec_mark_modulo = 1000;
1892         } else if (range <= 16 * fr) { /* 8-16 seconds */
1893                 minsec_mark_interval =  fr; /* show 1 seconds */
1894                 minsec_ruler_scale = minsec_show_seconds;
1895                 minsec_mark_modulo = 2;
1896         } else if (range <= 30 * fr) { /* 10-30 seconds */
1897                 minsec_mark_interval =  fr; /* show 1 seconds */
1898                 minsec_ruler_scale = minsec_show_seconds;
1899                 minsec_mark_modulo = 5;
1900         } else if (range <= 60 * fr) { /* 30-60 seconds */
1901                 minsec_mark_interval = fr; /* show 1 seconds */
1902                 minsec_ruler_scale = minsec_show_seconds;
1903                 minsec_mark_modulo = 5;
1904         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
1905                 minsec_mark_interval = 5 * fr; /* show 5 seconds */
1906                 minsec_ruler_scale = minsec_show_seconds;
1907                 minsec_mark_modulo = 3;
1908         } else if (range <= 4 * 60 * fr) { /* 4 minutes */
1909                 minsec_mark_interval = 5 * fr; /* show 10 seconds */
1910                 minsec_ruler_scale = minsec_show_seconds;
1911                 minsec_mark_modulo = 30;
1912         } else if (range <= 10 * 60 * fr) { /* 10 minutes */
1913                 minsec_mark_interval = 30 * fr; /* show 30 seconds */
1914                 minsec_ruler_scale = minsec_show_seconds;
1915                 minsec_mark_modulo = 120;
1916         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
1917                 minsec_mark_interval =  60 * fr; /* show 1 minute */
1918                 minsec_ruler_scale = minsec_show_minutes;
1919                 minsec_mark_modulo = 5;
1920         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
1921                 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
1922                 minsec_ruler_scale = minsec_show_minutes;
1923                 minsec_mark_modulo = 10;
1924         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
1925                 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
1926                 minsec_ruler_scale = minsec_show_minutes;
1927                 minsec_mark_modulo = 30;
1928         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
1929                 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
1930                 minsec_ruler_scale = minsec_show_minutes;
1931                 minsec_mark_modulo = 60;
1932         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
1933                 minsec_mark_interval =  60 * 60 * fr; /* show 60 minutes */
1934                 minsec_ruler_scale = minsec_show_hours;
1935                 minsec_mark_modulo = 2;
1936         } else {
1937                                                                                                                    
1938                 /* not possible if nframes64_t is a 32 bit quantity */
1939                                                                                                                    
1940                 minsec_mark_interval = 4 * 60 * 60 * fr; /* show 4 hrs */
1941         }
1942         minsec_nmarks = 2 + (range / minsec_mark_interval);
1943 }
1944
1945 gint
1946 Editor::metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
1947 {
1948         nframes64_t pos;
1949         nframes64_t spacer;
1950         long hrs, mins, secs, millisecs;
1951         gchar buf[16];
1952         gint n;
1953
1954         if (session == 0) {
1955                 return 0;
1956         }
1957
1958         /* to prevent 'flashing' */
1959         if (lower > (spacer = (nframes64_t)(128 * Editor::get_current_zoom ()))) {
1960                 lower = lower - spacer;
1961         } else {
1962                 lower = 0;
1963         }
1964
1965         *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * minsec_nmarks);
1966         pos = ((((nframes64_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
1967         switch (minsec_ruler_scale) {
1968         case minsec_show_seconds:
1969                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1970                         sample_to_clock_parts (pos, session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1971                         if (secs % minsec_mark_modulo == 0) {
1972                                 if (secs == 0) {
1973                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1974                                 } else {
1975                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1976                                 }
1977                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1978                         } else {
1979                                 snprintf (buf, sizeof(buf), " ");
1980                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
1981                         }
1982                         (*marks)[n].label = g_strdup (buf);
1983                         (*marks)[n].position = pos;
1984                 }
1985           break;
1986         case minsec_show_minutes:
1987                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
1988                         sample_to_clock_parts (pos, session->frame_rate(), &hrs, &mins, &secs, &millisecs);
1989                         if (mins % minsec_mark_modulo == 0) {
1990                                 if (mins == 0) {
1991                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
1992                                 } else {
1993                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
1994                                 }
1995                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
1996                         } else {
1997                                 snprintf (buf, sizeof(buf), " ");
1998                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
1999                         }
2000                         (*marks)[n].label = g_strdup (buf);
2001                         (*marks)[n].position = pos;
2002                 }
2003           break;
2004         case minsec_show_hours:
2005                  for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
2006                         sample_to_clock_parts (pos, session->frame_rate(), &hrs, &mins, &secs, &millisecs);
2007                         if (hrs % minsec_mark_modulo == 0) {
2008                                 (*marks)[n].style = GtkCustomRulerMarkMajor;
2009                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
2010                         } else {
2011                                 snprintf (buf, sizeof(buf), " ");
2012                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
2013                         }
2014                         (*marks)[n].label = g_strdup (buf);
2015                         (*marks)[n].position = pos;
2016                 }
2017               break;
2018         case minsec_show_frames:
2019                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
2020                         sample_to_clock_parts (pos, session->frame_rate(), &hrs, &mins, &secs, &millisecs);
2021                         if (millisecs % minsec_mark_modulo == 0) {
2022                                 if (secs == 0) {
2023                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
2024                                 } else {
2025                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
2026                                 }
2027                                 snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
2028                         } else {
2029                                 snprintf (buf, sizeof(buf), " ");
2030                                 (*marks)[n].style = GtkCustomRulerMarkMicro;
2031                         }
2032                         (*marks)[n].label = g_strdup (buf);
2033                         (*marks)[n].position = pos;
2034                 }
2035           break;
2036         }
2037
2038         return minsec_nmarks;
2039 }