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