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