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