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