enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / gtk2_ardour / editor_rulers.cc
index a7883674cd5495348137f698f37de71bffeac92a..868b00690083be3eca49a8b14ed4049d79a65a9e 100644 (file)
 
 #include <gtk/gtkaction.h>
 
+#include "canvas/container.h"
+#include "canvas/canvas.h"
+#include "canvas/ruler.h"
+#include "canvas/debug.h"
+#include "canvas/scroll_group.h"
+
 #include "ardour/session.h"
 #include "ardour/tempo.h"
 #include "ardour/profile.h"
 
 #include "gtkmm2ext/gtk_ui.h"
+#include "gtkmm2ext/keyboard.h"
 
+#include "ardour_ui.h"
 #include "editor.h"
 #include "editing.h"
 #include "actions.h"
-#include "gtk-custom-hruler.h"
 #include "gui_thread.h"
+#include "ruler_dialog.h"
 #include "time_axis_view.h"
 #include "editor_drag.h"
 #include "editor_cursors.h"
+#include "ui_config.h"
 
-#include "i18n.h"
+#include "pbd/i18n.h"
 
 using namespace ARDOUR;
 using namespace PBD;
 using namespace Gtk;
 using namespace Editing;
 
-Editor *Editor::ruler_editor;
-
 /* the order here must match the "metric" enums in editor.h */
 
-GtkCustomMetric Editor::ruler_metrics[4] = {
-       {1, Editor::_metric_get_timecode },
-       {1, Editor::_metric_get_bbt },
-       {1, Editor::_metric_get_samples },
-       {1, Editor::_metric_get_minsec }
-};
-
-void
-Editor::initialize_rulers ()
+class TimecodeMetric : public ArdourCanvas::Ruler::Metric
 {
-       ruler_editor = this;
-       ruler_grabbed_widget = 0;
-
-       _ruler_separator = new Gtk::HSeparator();
-       _ruler_separator->set_size_request(-1, 2);
-       _ruler_separator->set_name("TimebarPadding");
-       _ruler_separator->show();
-
-       _minsec_ruler = gtk_custom_hruler_new ();
-       minsec_ruler = Glib::wrap (_minsec_ruler);
-       minsec_ruler->set_name ("MinSecRuler");
-       minsec_ruler->set_size_request (-1, (int)timebar_height);
-       gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_minsec_ruler), &ruler_metrics[ruler_metric_minsec]);
-       minsec_ruler->hide ();
-       minsec_ruler->set_no_show_all();
-
-       _timecode_ruler = gtk_custom_hruler_new ();
-       timecode_ruler = Glib::wrap (_timecode_ruler);
-       timecode_ruler->set_name ("TimecodeRuler");
-       timecode_ruler->set_size_request (-1, (int)timebar_height);
-       gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_timecode_ruler), &ruler_metrics[ruler_metric_timecode]);
-       timecode_ruler->hide ();
-       timecode_ruler->set_no_show_all();
-       timecode_nmarks = 0;
-
-       _bbt_ruler = gtk_custom_hruler_new ();
-       bbt_ruler = Glib::wrap (_bbt_ruler);
-       bbt_ruler->set_name ("BBTRuler");
-       bbt_ruler->set_size_request (-1, (int)timebar_height);
-       gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_bbt_ruler), &ruler_metrics[ruler_metric_bbt]);
-       bbt_ruler->hide ();
-       bbt_ruler->set_no_show_all();
-       bbt_nmarks = 0;
-
-       _samples_ruler = gtk_custom_hruler_new ();
-       samples_ruler = Glib::wrap (_samples_ruler);
-       samples_ruler->set_name ("SamplesRuler");
-       samples_ruler->set_size_request (-1, (int) timebar_height);
-       gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER (_samples_ruler), &ruler_metrics[ruler_metric_samples]);
-       samples_ruler->hide ();
-       samples_ruler->set_no_show_all ();
-
-       _bbt_ruler = gtk_custom_hruler_new ();
-       bbt_ruler = Glib::wrap (_bbt_ruler);
-       bbt_ruler->set_name ("BBTRuler");
-       bbt_ruler->set_size_request (-1, (int)timebar_height);
-       gtk_custom_ruler_set_metric (GTK_CUSTOM_RULER(_bbt_ruler), &ruler_metrics[ruler_metric_bbt]);
-       bbt_ruler->hide ();
-       bbt_ruler->set_no_show_all();
-       minsec_ruler->hide ();
-       minsec_ruler->set_no_show_all();
-       minsec_nmarks = 0;
-
-       using namespace Box_Helpers;
-       BoxList & ruler_lab_children =  ruler_label_vbox.children();
-       BoxList & ruler_children =  time_canvas_vbox.children();
-       BoxList & lab_children =  time_button_vbox.children();
+    public:
+       TimecodeMetric (Editor* e) : _editor (e) {}
 
-       BoxList::iterator canvaspos = ruler_children.begin();
-
-       lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
-       lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
-
-       ruler_lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
-       ruler_children.insert (canvaspos, Element(*minsec_ruler, PACK_SHRINK, PACK_START));
-       ruler_lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
-       ruler_children.insert (canvaspos, Element(*timecode_ruler, PACK_SHRINK, PACK_START));
-       ruler_lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
-       ruler_children.insert (canvaspos, Element (*samples_ruler, PACK_SHRINK, PACK_START));
-       ruler_lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
-       ruler_children.insert (canvaspos, Element(*bbt_ruler, PACK_SHRINK, PACK_START));
-
-       timecode_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
-       bbt_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
-       samples_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
-       minsec_ruler->add_events (Gdk::BUTTON_PRESS_MASK|Gdk::BUTTON_RELEASE_MASK|Gdk::SCROLL_MASK);
-
-       timecode_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
-       bbt_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
-       samples_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
-       minsec_ruler->signal_button_release_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_release));
-
-       timecode_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
-       bbt_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
-       samples_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
-       minsec_ruler->signal_button_press_event().connect (sigc::mem_fun(*this, &Editor::ruler_button_press));
-
-       timecode_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
-       bbt_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
-       samples_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
-       minsec_ruler->signal_motion_notify_event().connect (sigc::mem_fun(*this, &Editor::ruler_mouse_motion));
-
-       timecode_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
-       bbt_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
-       samples_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
-       minsec_ruler->signal_scroll_event().connect (sigc::mem_fun(*this, &Editor::ruler_scroll));
+       void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+               _editor->metric_get_timecode (marks, lower, upper, maxchars);
+       }
 
-       visible_timebars = 0; /*this will be changed below */
-       canvas_timebars_vsize = 0;
-}
+    private:
+       Editor* _editor;
+};
 
-bool
-Editor::ruler_scroll (GdkEventScroll* event)
+class SamplesMetric : public ArdourCanvas::Ruler::Metric
 {
-       framepos_t xdelta;
-       int direction = event->direction;
-       bool handled = false;
-
-       switch (direction) {
-       case GDK_SCROLL_UP:
-               temporal_zoom_step (false);
-               handled = true;
-               break;
+    public:
+       SamplesMetric (Editor* e) : _editor (e) {}
 
-       case GDK_SCROLL_DOWN:
-               temporal_zoom_step (true);
-               handled = true;
-               break;
+       void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+               _editor->metric_get_samples (marks, lower, upper, maxchars);
+       }
 
-       case GDK_SCROLL_LEFT:
-               xdelta = (current_page_frames() / 2);
-               if (leftmost_frame > xdelta) {
-                       reset_x_origin (leftmost_frame - xdelta);
-               } else {
-                       reset_x_origin (0);
-               }
-               handled = true;
-               break;
+    private:
+       Editor* _editor;
+};
 
-       case GDK_SCROLL_RIGHT:
-               xdelta = (current_page_frames() / 2);
-               if (max_framepos - xdelta > leftmost_frame) {
-                       reset_x_origin (leftmost_frame + xdelta);
-               } else {
-                       reset_x_origin (max_framepos - current_page_frames());
-               }
-               handled = true;
-               break;
+class BBTMetric : public ArdourCanvas::Ruler::Metric
+{
+    public:
+       BBTMetric (Editor* e) : _editor (e) {}
 
-       default:
-               /* what? */
-               break;
+       void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+               _editor->metric_get_bbt (marks, lower, upper, maxchars);
        }
 
-       return handled;
-}
-
+    private:
+       Editor* _editor;
+};
 
-bool
-Editor::ruler_button_press (GdkEventButton* ev)
+class MinsecMetric : public ArdourCanvas::Ruler::Metric
 {
-       if (_session == 0) {
-               return false;
+    public:
+       MinsecMetric (Editor* e) : _editor (e) {}
+
+       void get_marks (std::vector<ArdourCanvas::Ruler::Mark>& marks, double lower, double upper, int maxchars) const {
+               _editor->metric_get_minsec (marks, lower, upper, maxchars);
        }
 
-       Widget * grab_widget = 0;
+    private:
+       Editor* _editor;
+};
 
-       if (timecode_ruler->is_realized() && ev->window == timecode_ruler->get_window()->gobj()) {
-               grab_widget = timecode_ruler;
-       } else if (bbt_ruler->is_realized() && ev->window == bbt_ruler->get_window()->gobj()) {
-               grab_widget = bbt_ruler;
-       } else if (samples_ruler->is_realized() && ev->window == samples_ruler->get_window()->gobj()) {
-               grab_widget = samples_ruler;
-       } else if (minsec_ruler->is_realized() && ev->window == minsec_ruler->get_window()->gobj()) {
-               grab_widget = minsec_ruler;
-       }
+static ArdourCanvas::Ruler::Metric* _bbt_metric;
+static ArdourCanvas::Ruler::Metric* _timecode_metric;
+static ArdourCanvas::Ruler::Metric* _samples_metric;
+static ArdourCanvas::Ruler::Metric* _minsec_metric;
 
-       if (grab_widget) {
-               grab_widget->add_modal_grab ();
-               ruler_grabbed_widget = grab_widget;
-       }
+void
+Editor::initialize_rulers ()
+{
+       ruler_grabbed_widget = 0;
 
-       if (ev->button == 1) {
-               // Since we will locate the playhead on button release, cancel any running
-               // auditions.
-               if (_session->is_auditioning()) {
-                       _session->cancel_audition ();
-               }
+       Pango::FontDescription font (UIConfiguration::instance().get_SmallerFont());
 
-               /* playhead cursor */
-               _drags->set (new CursorDrag (this, &playhead_cursor->canvas_item, false), reinterpret_cast<GdkEvent *> (ev));
-               _dragging_playhead = true;
-       }
+       _timecode_metric = new TimecodeMetric (this);
+       _bbt_metric = new BBTMetric (this);
+       _minsec_metric = new MinsecMetric (this);
+       _samples_metric = new SamplesMetric (this);
 
-       return true;
-}
+       timecode_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_timecode_metric,
+                                                 ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+       timecode_ruler->set_font_description (font);
+       CANVAS_DEBUG_NAME (timecode_ruler, "timecode ruler");
+       timecode_nmarks = 0;
 
-bool
-Editor::ruler_button_release (GdkEventButton* ev)
-{
-       if (_session == 0) {
-               return false;
-       }
+       samples_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_samples_metric,
+                                                ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+       samples_ruler->set_font_description (font);
+       CANVAS_DEBUG_NAME (samples_ruler, "samples ruler");
 
-       gint x,y;
-       Gdk::ModifierType state;
+       minsec_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_minsec_metric,
+                                               ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+       minsec_ruler->set_font_description (font);
+       CANVAS_DEBUG_NAME (minsec_ruler, "minsec ruler");
+       minsec_nmarks = 0;
 
-       if (_drags->active ()) {
-               _drags->end_grab (reinterpret_cast<GdkEvent*> (ev));
-               _dragging_playhead = false;
-       }
+       bbt_ruler = new ArdourCanvas::Ruler (_time_markers_group, *_bbt_metric,
+                                            ArdourCanvas::Rect (0, 0, ArdourCanvas::COORD_MAX, timebar_height));
+       bbt_ruler->set_font_description (font);
+       CANVAS_DEBUG_NAME (bbt_ruler, "bbt ruler");
+       timecode_nmarks = 0;
 
-       if (ev->button == 3) {
-               /* need to use the correct x,y, the event lies */
-               time_canvas_event_box.get_window()->get_pointer (x, y, state);
+       using namespace Box_Helpers;
+       BoxList & lab_children =  time_bars_vbox.children();
 
-               stop_canvas_autoscroll();
+       lab_children.push_back (Element(minsec_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(timecode_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(samples_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(bbt_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(meter_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(tempo_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(range_mark_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(transport_mark_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(cd_mark_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(mark_label, PACK_SHRINK, PACK_START));
+       lab_children.push_back (Element(videotl_label, PACK_SHRINK, PACK_START));
 
-               framepos_t where = leftmost_frame + pixel_to_frame (x);
-               snap_to (where);
-               popup_ruler_menu (where);
-       }
+       /* 1 event handler to bind them all ... */
 
-       if (ruler_grabbed_widget) {
-               ruler_grabbed_widget->remove_modal_grab();
-               ruler_grabbed_widget = 0;
-       }
+       timecode_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), timecode_ruler, TimecodeRulerItem));
+       minsec_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), minsec_ruler, MinsecRulerItem));
+       bbt_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), bbt_ruler, BBTRulerItem));
+       samples_ruler->Event.connect (sigc::bind (sigc::mem_fun (*this, &Editor::canvas_ruler_event), samples_ruler, SamplesRulerItem));
 
-       return true;
+       visible_timebars = 0; /*this will be changed below */
 }
 
 bool
 Editor::ruler_label_button_release (GdkEventButton* ev)
 {
-       if (ev->button == 3) {
-               Gtk::Menu* m = dynamic_cast<Gtk::Menu*> (ActionManager::get_widget (X_("/RulerMenuPopup")));
-               if (m) {
-                       m->popup (1, ev->time);
+       if (Gtkmm2ext::Keyboard::is_context_menu_event (ev)) {
+               if (!ruler_dialog) {
+                       ruler_dialog = new RulerDialog ();
                }
+               ruler_dialog->present ();
        }
 
        return true;
 }
 
-
-bool
-Editor::ruler_mouse_motion (GdkEventMotion* ev)
-{
-       if (_session == 0) {
-               return false;
-       }
-
-       if (_drags->active ()) {
-               _drags->motion_handler (reinterpret_cast<GdkEvent*> (ev), false);
-       }
-
-       return true;
-}
-
-
 void
 Editor::popup_ruler_menu (framepos_t where, ItemType t)
 {
@@ -337,119 +209,83 @@ Editor::popup_ruler_menu (framepos_t where, ItemType t)
 
        switch (t) {
        case MarkerBarItem:
-               ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false, false)));
+               ruler_items.push_back (MenuElem (_("New location marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, false)));
                ruler_items.push_back (MenuElem (_("Clear all locations"), sigc::mem_fun(*this, &Editor::clear_markers)));
                ruler_items.push_back (MenuElem (_("Unhide locations"), sigc::mem_fun(*this, &Editor::unhide_markers)));
-               ruler_items.push_back (SeparatorElem ());
                break;
+
        case RangeMarkerBarItem:
                ruler_items.push_back (MenuElem (_("New range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_range), where)));
                ruler_items.push_back (MenuElem (_("Clear all ranges"), sigc::mem_fun(*this, &Editor::clear_ranges)));
                ruler_items.push_back (MenuElem (_("Unhide ranges"), sigc::mem_fun(*this, &Editor::unhide_ranges)));
-               ruler_items.push_back (SeparatorElem ());
-
                break;
-       case TransportMarkerBarItem:
 
+       case TransportMarkerBarItem:
+               ruler_items.push_back (MenuElem (_("New Loop range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_loop), where)));
+               ruler_items.push_back (MenuElem (_("New Punch range"), sigc::bind (sigc::mem_fun (*this, &Editor::mouse_add_new_punch), where)));
                break;
 
        case CdMarkerBarItem:
                // TODO
-               ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true, false)));
+               ruler_items.push_back (MenuElem (_("New CD track marker"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_marker), where, true)));
                break;
 
-
        case TempoBarItem:
                ruler_items.push_back (MenuElem (_("New Tempo"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_tempo_event), where)));
-               ruler_items.push_back (SeparatorElem ());
                break;
 
        case MeterBarItem:
                ruler_items.push_back (MenuElem (_("New Meter"), sigc::bind ( sigc::mem_fun(*this, &Editor::mouse_add_new_meter_event), where)));
-               ruler_items.push_back (SeparatorElem ());
                break;
 
        case VideoBarItem:
-               ruler_items.push_back (MenuElem (_("Timeline height")));
-               static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
+               /* proper headings would be nice
+                * but AFAICT the only way to get them will be to define a
+                * special GTK style for insensitive Elements or subclass MenuItem
+                */
+               //ruler_items.push_back (MenuElem (_("Timeline height"))); // heading
+               //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
                ruler_items.push_back (CheckMenuElem (_("Large"),  sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 6)));
                if (videotl_bar_height == 6) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
                ruler_items.push_back (CheckMenuElem (_("Normal"), sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 4)));
                if (videotl_bar_height == 4) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
                ruler_items.push_back (CheckMenuElem (_("Small"),  sigc::bind ( sigc::mem_fun(*this, &Editor::set_video_timeline_height), 3)));
                if (videotl_bar_height == 3) { static_cast<Gtk::CheckMenuItem*>(&ruler_items.back())->set_active(true);}
-               ruler_items.push_back (SeparatorElem ());
 
-               ruler_items.push_back (MenuElem (_("Align Video Track")));
-               static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
+               ruler_items.push_back (SeparatorElem ());
 
+               //ruler_items.push_back (MenuElem (_("Align Video Track"))); // heading
+               //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
                ruler_items.push_back (CheckMenuElem (_("Lock")));
                {
-               Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
-               vtl_lock->set_active(is_video_timeline_locked());
-               vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
+                       Gtk::CheckMenuItem* vtl_lock = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
+                       vtl_lock->set_active(is_video_timeline_locked());
+                       vtl_lock->signal_activate().connect (sigc::mem_fun(*this, &Editor::toggle_video_timeline_locked));
                }
 
                ruler_items.push_back (SeparatorElem ());
+
+               //ruler_items.push_back (MenuElem (_("Video Monitor"))); // heading
+               //static_cast<MenuItem*>(&ruler_items.back())->set_sensitive(false);
+               ruler_items.push_back (CheckMenuElem (_("Video Monitor")));
+               {
+                       Gtk::CheckMenuItem* xjadeo_toggle = static_cast<Gtk::CheckMenuItem*>(&ruler_items.back());
+                       if (!ARDOUR_UI::instance()->video_timeline->found_xjadeo()) {
+                               xjadeo_toggle->set_sensitive(false);
+                       }
+                       xjadeo_toggle->set_active(xjadeo_proc_action->get_active());
+                       xjadeo_toggle->signal_activate().connect (sigc::bind(sigc::mem_fun(*this, &Editor::toggle_xjadeo_proc), -1));
+               }
                break;
 
        default:
                break;
        }
 
-       Glib::RefPtr<Action> action;
-
-       action = ActionManager::get_action ("Rulers", "toggle-minsec-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       if (!Profile->get_sae()) {
-               action = ActionManager::get_action ("Rulers", "toggle-timecode-ruler");
-               if (action) {
-                       ruler_items.push_back (MenuElem (*action->create_menu_item()));
-               }
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-samples-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-bbt-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-meter-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-tempo-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       if (!Profile->get_sae()) {
-               action = ActionManager::get_action ("Rulers", "toggle-range-ruler");
-               if (action) {
-                       ruler_items.push_back (MenuElem (*action->create_menu_item()));
-               }
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-loop-punch-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-cd-marker-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-marker-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
-       }
-       action = ActionManager::get_action ("Rulers", "toggle-video-ruler");
-       if (action) {
-               ruler_items.push_back (MenuElem (*action->create_menu_item()));
+       if (!ruler_items.empty()) {
+               editor_ruler_menu->popup (1, gtk_get_current_event_time());
        }
 
-       editor_ruler_menu->popup (1, gtk_get_current_event_time());
-
        no_ruler_shown_update = false;
 }
 
@@ -477,7 +313,7 @@ Editor::store_ruler_visibility ()
 void
 Editor::restore_ruler_visibility ()
 {
-       XMLProperty* prop;
+       XMLProperty const * prop;
        XMLNode * node = _session->extra_xml (X_("RulerVisibility"));
 
        no_ruler_shown_update = true;
@@ -586,116 +422,129 @@ Editor::restore_ruler_visibility ()
 void
 Editor::update_ruler_visibility ()
 {
-       int visible_rulers = 0;
+       int visible_timebars = 0;
 
        if (no_ruler_shown_update) {
                return;
        }
 
-       visible_timebars = 0;
+       /* the order of the timebars is fixed, so we have to go through each one
+        * and adjust its position depending on what is shown.
+        *
+        * Order: minsec, timecode, samples, bbt, meter, tempo, ranges,
+        * loop/punch, cd markers, location markers
+        */
+
+       double tbpos = 0.0;
+       double tbgpos = 0.0;
+       double old_unit_pos;
+
+#ifdef __APPLE__
+       /* gtk update probs require this (damn) */
+       meter_label.hide();
+       tempo_label.hide();
+       range_mark_label.hide();
+       transport_mark_label.hide();
+       cd_mark_label.hide();
+       mark_label.hide();
+       videotl_label.hide();
+#endif
 
        if (ruler_minsec_action->get_active()) {
-               visible_rulers++;
-               minsec_label.show ();
-               minsec_ruler->show ();
+               old_unit_pos = minsec_ruler->position().y;
+               if (tbpos != old_unit_pos) {
+                       minsec_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+               }
+               minsec_ruler->show();
+               minsec_label.show();
+               tbpos += timebar_height;
+               tbgpos += timebar_height;
+               visible_timebars++;
        } else {
-               minsec_label.hide ();
-               minsec_ruler->hide ();
+               minsec_ruler->hide();
+               minsec_label.hide();
        }
 
        if (ruler_timecode_action->get_active()) {
-               visible_rulers++;
-               timecode_label.show ();
-               timecode_ruler->show ();
+               old_unit_pos = timecode_ruler->position().y;
+               if (tbpos != old_unit_pos) {
+                       timecode_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+               }
+               timecode_ruler->show();
+               timecode_label.show();
+               tbpos += timebar_height;
+               tbgpos += timebar_height;
+               visible_timebars++;
        } else {
-               timecode_label.hide ();
-               timecode_ruler->hide ();
+               timecode_ruler->hide();
+               timecode_label.hide();
        }
 
        if (ruler_samples_action->get_active()) {
-               visible_rulers++;
-               samples_label.show ();
-               samples_ruler->show ();
+               old_unit_pos = samples_ruler->position().y;
+               if (tbpos != old_unit_pos) {
+                       samples_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+               }
+               samples_ruler->show();
+               samples_label.show();
+               tbpos += timebar_height;
+               tbgpos += timebar_height;
+               visible_timebars++;
        } else {
-               samples_label.hide ();
-               samples_ruler->hide ();
+               samples_ruler->hide();
+               samples_label.hide();
        }
 
        if (ruler_bbt_action->get_active()) {
-               visible_rulers++;
-               bbt_label.show ();
-               bbt_ruler->show ();
+               old_unit_pos = bbt_ruler->position().y;
+               if (tbpos != old_unit_pos) {
+                       bbt_ruler->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
+               }
+               bbt_ruler->show();
+               bbt_label.show();
+               tbpos += timebar_height;
+               tbgpos += timebar_height;
+               visible_timebars++;
        } else {
-               bbt_label.hide ();
-               bbt_ruler->hide ();
+               bbt_ruler->hide();
+               bbt_label.hide();
        }
 
-       double tbpos = 0.0;
-       double tbgpos = 0.0;
-       double old_unit_pos;
-
-#ifdef GTKOSX
-       /* gtk update probs require this (damn) */
-       meter_label.hide();
-       tempo_label.hide();
-       range_mark_label.hide();
-       transport_mark_label.hide();
-       cd_mark_label.hide();
-       mark_label.hide();
-       videotl_label.hide();
-#endif
        if (ruler_meter_action->get_active()) {
-               old_unit_pos = meter_group->property_y();
+               old_unit_pos = meter_group->position().y;
                if (tbpos != old_unit_pos) {
-                       meter_group->move ( 0.0, tbpos - old_unit_pos);
-               }
-               old_unit_pos = meter_bar_group->property_y();
-               if (tbgpos != old_unit_pos) {
-                       meter_bar_group->move ( 0.0, tbgpos - old_unit_pos);
+                       meter_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               meter_bar_group->show();
                meter_group->show();
                meter_label.show();
                tbpos += timebar_height;
                tbgpos += timebar_height;
                visible_timebars++;
        } else {
-               meter_bar_group->hide();
                meter_group->hide();
                meter_label.hide();
        }
 
        if (ruler_tempo_action->get_active()) {
-               old_unit_pos = tempo_group->property_y();
+               old_unit_pos = tempo_group->position().y;
                if (tbpos != old_unit_pos) {
-                       tempo_group->move(0.0, tbpos - old_unit_pos);
+                       tempo_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               old_unit_pos = tempo_bar_group->property_y();
-               if (tbgpos != old_unit_pos) {
-                       tempo_bar_group->move ( 0.0, tbgpos - old_unit_pos);
-               }
-               tempo_bar_group->show();
                tempo_group->show();
                tempo_label.show();
                tbpos += timebar_height;
                tbgpos += timebar_height;
                visible_timebars++;
        } else {
-               tempo_bar_group->hide();
                tempo_group->hide();
                tempo_label.hide();
        }
 
-       if (!Profile->get_sae() && ruler_range_action->get_active()) {
-               old_unit_pos = range_marker_group->property_y();
+       if (ruler_range_action->get_active()) {
+               old_unit_pos = range_marker_group->position().y;
                if (tbpos != old_unit_pos) {
-                       range_marker_group->move (0.0, tbpos - old_unit_pos);
-               }
-               old_unit_pos = range_marker_bar_group->property_y();
-               if (tbgpos != old_unit_pos) {
-                       range_marker_bar_group->move (0.0, tbgpos - old_unit_pos);
+                       range_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               range_marker_bar_group->show();
                range_marker_group->show();
                range_mark_label.show();
 
@@ -703,42 +552,30 @@ Editor::update_ruler_visibility ()
                tbgpos += timebar_height;
                visible_timebars++;
        } else {
-               range_marker_bar_group->hide();
                range_marker_group->hide();
                range_mark_label.hide();
        }
 
        if (ruler_loop_punch_action->get_active()) {
-               old_unit_pos = transport_marker_group->property_y();
+               old_unit_pos = transport_marker_group->position().y;
                if (tbpos != old_unit_pos) {
-                       transport_marker_group->move ( 0.0, tbpos - old_unit_pos);
-               }
-               old_unit_pos = transport_marker_bar_group->property_y();
-               if (tbgpos != old_unit_pos) {
-                       transport_marker_bar_group->move ( 0.0, tbgpos - old_unit_pos);
+                       transport_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               transport_marker_bar_group->show();
                transport_marker_group->show();
                transport_mark_label.show();
                tbpos += timebar_height;
                tbgpos += timebar_height;
                visible_timebars++;
        } else {
-               transport_marker_bar_group->hide();
                transport_marker_group->hide();
                transport_mark_label.hide();
        }
 
        if (ruler_cd_marker_action->get_active()) {
-               old_unit_pos = cd_marker_group->property_y();
+               old_unit_pos = cd_marker_group->position().y;
                if (tbpos != old_unit_pos) {
-                       cd_marker_group->move (0.0, tbpos - old_unit_pos);
+                       cd_marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               old_unit_pos = cd_marker_bar_group->property_y();
-               if (tbgpos != old_unit_pos) {
-                       cd_marker_bar_group->move (0.0, tbgpos - old_unit_pos);
-               }
-               cd_marker_bar_group->show();
                cd_marker_group->show();
                cd_mark_label.show();
                tbpos += timebar_height;
@@ -747,7 +584,6 @@ Editor::update_ruler_visibility ()
                // make sure all cd markers show up in their respective places
                update_cd_marker_display();
        } else {
-               cd_marker_bar_group->hide();
                cd_marker_group->hide();
                cd_mark_label.hide();
                // make sure all cd markers show up in their respective places
@@ -755,74 +591,43 @@ Editor::update_ruler_visibility ()
        }
 
        if (ruler_marker_action->get_active()) {
-               old_unit_pos = marker_group->property_y();
+               old_unit_pos = marker_group->position().y;
                if (tbpos != old_unit_pos) {
-                       marker_group->move ( 0.0, tbpos - old_unit_pos);
-               }
-               old_unit_pos = marker_bar_group->property_y();
-               if (tbgpos != old_unit_pos) {
-                       marker_bar_group->move ( 0.0, tbgpos - old_unit_pos);
+                       marker_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               marker_bar_group->show();
                marker_group->show();
                mark_label.show();
                tbpos += timebar_height;
                tbgpos += timebar_height;
                visible_timebars++;
        } else {
-               marker_bar_group->hide();
                marker_group->hide();
                mark_label.hide();
        }
 
        if (ruler_video_action->get_active()) {
-               old_unit_pos = videotl_group->property_y();
+               old_unit_pos = videotl_group->position().y;
                if (tbpos != old_unit_pos) {
-                       videotl_group->move ( 0.0, tbpos - old_unit_pos);
-               }
-               old_unit_pos = videotl_bar_group->property_y();
-               if (tbgpos != old_unit_pos) {
-                       videotl_bar_group->move ( 0.0, tbgpos - old_unit_pos);
+                       videotl_group->move (ArdourCanvas::Duple (0.0, tbpos - old_unit_pos));
                }
-               videotl_bar_group->show();
                videotl_group->show();
                videotl_label.show();
                tbpos += timebar_height * videotl_bar_height;
                tbgpos += timebar_height * videotl_bar_height;
                visible_timebars+=videotl_bar_height;
-         queue_visual_videotimeline_update();
+               queue_visual_videotimeline_update();
        } else {
-               videotl_bar_group->hide();
                videotl_group->hide();
                videotl_label.hide();
-         update_video_timeline(true);
+               update_video_timeline(true);
        }
 
-       gdouble old_canvas_timebars_vsize = canvas_timebars_vsize;
-       canvas_timebars_vsize = (timebar_height * visible_timebars) - 1;
-       gdouble vertical_pos_delta = canvas_timebars_vsize - old_canvas_timebars_vsize;
-       vertical_adjustment.set_upper(vertical_adjustment.get_upper() + vertical_pos_delta);
-       full_canvas_height += vertical_pos_delta;
+       time_bars_vbox.set_size_request (-1, (int)(timebar_height * visible_timebars));
 
-       if (vertical_adjustment.get_value() != 0 && (vertical_adjustment.get_value() + _canvas_height >= full_canvas_height)) {
-               /*if we're at the bottom of the canvas, don't move the _trackview_group*/
-               vertical_adjustment.set_value (full_canvas_height - _canvas_height + 1);
-       } else {
-               _trackview_group->property_y () = - get_trackview_group_vertical_offset ();
-               _background_group->property_y () = - get_trackview_group_vertical_offset ();
-               _trackview_group->move (0, 0);
-               _background_group->move (0, 0);
-               last_trackview_group_vertical_offset = get_trackview_group_vertical_offset ();
-       }
-
-       gdouble bottom_track_pos = vertical_adjustment.get_value() + _canvas_height - canvas_timebars_vsize;
-       std::pair<TimeAxisView*, int> const p = trackview_by_y_position (bottom_track_pos);
-       if (p.first) {
-               p.first->clip_to_viewport ();
-       }
+       /* move hv_scroll_group (trackviews) to the end of the timebars
+        */
 
-       ruler_label_vbox.set_size_request (-1, (int)(timebar_height * visible_rulers));
-       time_canvas_vbox.set_size_request (-1,-1);
+       hv_scroll_group->set_y_position (timebar_height * visible_timebars);
 
        compute_fixed_ruler_scale ();
        update_fixed_rulers();
@@ -843,11 +648,10 @@ Editor::update_just_timecode ()
                return;
        }
 
-       framepos_t rightmost_frame = leftmost_frame + current_page_frames();
+       framepos_t rightmost_frame = leftmost_frame + current_page_samples();
 
        if (ruler_timecode_action->get_active()) {
-               gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_timecode_ruler), leftmost_frame, rightmost_frame,
-                                           leftmost_frame, _session->current_end_frame());
+               timecode_ruler->set_range (leftmost_frame, rightmost_frame);
        }
 }
 
@@ -859,15 +663,15 @@ Editor::compute_fixed_ruler_scale ()
        }
 
        if (ruler_timecode_action->get_active()) {
-               set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
+               set_timecode_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
        }
 
        if (ruler_minsec_action->get_active()) {
-               set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
+               set_minsec_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
        }
 
        if (ruler_samples_action->get_active()) {
-               set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_frames());
+               set_samples_ruler_scale (leftmost_frame, leftmost_frame + current_page_samples());
        }
 }
 
@@ -882,80 +686,51 @@ Editor::update_fixed_rulers ()
 
        compute_fixed_ruler_scale ();
 
-       ruler_metrics[ruler_metric_timecode].units_per_pixel = frames_per_unit;
-       ruler_metrics[ruler_metric_samples].units_per_pixel = frames_per_unit;
-       ruler_metrics[ruler_metric_minsec].units_per_pixel = frames_per_unit;
+       _timecode_metric->units_per_pixel = samples_per_pixel;
+       _samples_metric->units_per_pixel = samples_per_pixel;
+       _minsec_metric->units_per_pixel = samples_per_pixel;
 
-       rightmost_frame = leftmost_frame + current_page_frames();
+       rightmost_frame = leftmost_frame + current_page_samples();
 
        /* these force a redraw, which in turn will force execution of the metric callbacks
           to compute the relevant ticks to display.
        */
 
        if (ruler_timecode_action->get_active()) {
-               gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_timecode_ruler), leftmost_frame, rightmost_frame,
-                                           leftmost_frame, _session->current_end_frame());
+               timecode_ruler->set_range (leftmost_frame, rightmost_frame);
        }
 
        if (ruler_samples_action->get_active()) {
-               gtk_custom_ruler_set_range (GTK_CUSTOM_RULER (_samples_ruler), leftmost_frame, rightmost_frame,
-                                           leftmost_frame, _session->current_end_frame());
+               samples_ruler->set_range (leftmost_frame, rightmost_frame);
        }
 
        if (ruler_minsec_action->get_active()) {
-               gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_minsec_ruler), leftmost_frame, rightmost_frame,
-                                           leftmost_frame, _session->current_end_frame());
+               minsec_ruler->set_range (leftmost_frame, rightmost_frame);
        }
 }
 
 void
-Editor::update_tempo_based_rulers (ARDOUR::TempoMap::BBTPointList::const_iterator& begin,
-                                   ARDOUR::TempoMap::BBTPointList::const_iterator& end)
+Editor::update_tempo_based_rulers (std::vector<TempoMap::BBTPoint>& grid)
 {
        if (_session == 0) {
                return;
        }
 
-       compute_bbt_ruler_scale (leftmost_frame, leftmost_frame+current_page_frames(),
-                                begin, end);
+       compute_bbt_ruler_scale (grid, leftmost_frame, leftmost_frame+current_page_samples());
 
-       ruler_metrics[ruler_metric_bbt].units_per_pixel = frames_per_unit;
+       _bbt_metric->units_per_pixel = samples_per_pixel;
 
        if (ruler_bbt_action->get_active()) {
-               gtk_custom_ruler_set_range (GTK_CUSTOM_RULER(_bbt_ruler), leftmost_frame, leftmost_frame+current_page_frames(),
-                                           leftmost_frame, _session->current_end_frame());
+               bbt_ruler->set_range (leftmost_frame, leftmost_frame+current_page_samples());
        }
 }
 
-/* Mark generation */
-
-gint
-Editor::_metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
-       return ruler_editor->metric_get_timecode (marks, lower, upper, maxchars);
-}
-
-gint
-Editor::_metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
-       return ruler_editor->metric_get_bbt (marks, lower, upper, maxchars);
-}
-
-gint
-Editor::_metric_get_samples (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
-       return ruler_editor->metric_get_samples (marks, lower, upper, maxchars);
-}
-
-gint
-Editor::_metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint maxchars)
-{
-       return ruler_editor->metric_get_minsec (marks, lower, upper, maxchars);
-}
 
 void
 Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
 {
+        using namespace std;
+
        framepos_t spacer;
        framepos_t fr;
 
@@ -970,6 +745,7 @@ Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
        } else {
                lower = 0;
        }
+
        upper = upper + spacer;
        framecnt_t const range = upper - lower;
 
@@ -1043,26 +819,37 @@ Editor::set_timecode_ruler_scale (framepos_t lower, framepos_t upper)
                timecode_nmarks = 2 + 24;
        } else {
 
-               /* not possible if framepos_t is a 32 bit quantity */
+                const framecnt_t hours_in_range = range / (60 * 60 * fr);
+                const int text_width_rough_guess = 120; /* pixels, very very approximate guess at how wide the tick mark text is */
 
-               timecode_ruler_scale = timecode_show_hours;
-               timecode_mark_modulo = 4;
-               timecode_nmarks = 2 + 24;
-       }
+                /* Normally we do not need to know anything about the width of the canvas
+                   to set the ruler scale, because the caller has already determined
+                   the width and set lower + upper arguments to this function to match that.
 
+                   But in this case, where the range defined by lower and uppper can vary
+                   substantially (basically anything from 24hrs+ to several billion years)
+                   trying to decide which tick marks to show does require us to know
+                   about the available width.
+                */
+
+               timecode_nmarks = _track_canvas->width() / text_width_rough_guess;
+                timecode_ruler_scale = timecode_show_many_hours;
+                timecode_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / timecode_nmarks));
+       }
 }
 
-gint
-Editor::metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
+void
+Editor::metric_get_timecode (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
 {
        framepos_t pos;
        framecnt_t spacer;
        Timecode::Time timecode;
        gchar buf[16];
        gint n;
+       ArdourCanvas::Ruler::Mark mark;
 
        if (_session == 0) {
-               return 0;
+               return;
        }
 
        if (lower > (spacer = (framecnt_t)(128 * Editor::get_current_zoom ()))) {
@@ -1073,158 +860,174 @@ Editor::metric_get_timecode (GtkCustomRulerMark **marks, gdouble lower, gdouble
 
        pos = (framecnt_t) floor (lower);
 
-       *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * timecode_nmarks);
        switch (timecode_ruler_scale) {
        case timecode_show_bits:
-
                // Find timecode time of this sample (pos) with subframe accuracy
                _session->sample_to_timecode(pos, timecode, true /* use_offset */, true /* use_subframes */ );
-
                for (n = 0; n < timecode_nmarks; n++) {
                        _session->timecode_to_sample(timecode, pos, true /* use_offset */, true /* use_subframes */ );
                        if ((timecode.subframes % timecode_mark_modulo) == 0) {
                                if (timecode.subframes == 0) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                        snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                        snprintf (buf, sizeof(buf), ".%02u", timecode.subframes);
                                }
                        } else {
                                snprintf (buf, sizeof(buf)," ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
-
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                        }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = pos;
-
+                       mark.label = buf;
+                       mark.position = pos;
+                       marks.push_back (mark);
                        // Increment subframes by one
                        Timecode::increment_subframes( timecode, _session->config.get_subframes_per_frame() );
                }
-         break;
+               break;
+
+       case timecode_show_frames:
+               // Find timecode time of this sample (pos)
+               _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
+               // Go to next whole frame down
+               Timecode::frames_floor( timecode );
+               for (n = 0; n < timecode_nmarks; n++) {
+                       _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
+                       if ((timecode.frames % timecode_mark_modulo) == 0) {
+                               if (timecode.frames == 0) {
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
+                               } else {
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
+                               }
+                               mark.position = pos;
+                               snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
+                       } else {
+                               snprintf (buf, sizeof(buf)," ");
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                               mark.position = pos;
+                       }
+                       mark.label = buf;
+                       marks.push_back (mark);
+                       Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
+               }
+               break;
+
        case timecode_show_seconds:
                // Find timecode time of this sample (pos)
                _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
                // Go to next whole second down
                Timecode::seconds_floor( timecode );
-
                for (n = 0; n < timecode_nmarks; n++) {
                        _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
                        if ((timecode.seconds % timecode_mark_modulo) == 0) {
                                if (timecode.seconds == 0) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
-                                       (*marks)[n].position = pos;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
+                                       mark.position = pos;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
-                                       (*marks)[n].position = pos;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
+                                       mark.position = pos;
                                }
                                snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
                        } else {
                                snprintf (buf, sizeof(buf)," ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
-                               (*marks)[n].position = pos;
-
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                               mark.position = pos;
                        }
-                       (*marks)[n].label = g_strdup (buf);
+                       mark.label = buf;
+                       marks.push_back (mark);
                        Timecode::increment_seconds( timecode, _session->config.get_subframes_per_frame() );
                }
-         break;
+               break;
+
        case timecode_show_minutes:
-               // Find timecode time of this sample (pos)
+               //Find timecode time of this sample (pos)
                _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
                // Go to next whole minute down
                Timecode::minutes_floor( timecode );
-
                for (n = 0; n < timecode_nmarks; n++) {
                        _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
                        if ((timecode.minutes % timecode_mark_modulo) == 0) {
                                if (timecode.minutes == 0) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                }
                                snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
                        } else {
                                snprintf (buf, sizeof(buf)," ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
-
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                        }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = pos;
-
+                       mark.label = buf;
+                       mark.position = pos;
+                       marks.push_back (mark);
                        Timecode::increment_minutes( timecode, _session->config.get_subframes_per_frame() );
                }
-
-         break;
+               break;
        case timecode_show_hours:
                // Find timecode time of this sample (pos)
                _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
                // Go to next whole hour down
                Timecode::hours_floor( timecode );
-
                for (n = 0; n < timecode_nmarks; n++) {
                        _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
                        if ((timecode.hours % timecode_mark_modulo) == 0) {
-                               (*marks)[n].style = GtkCustomRulerMarkMajor;
+                               mark.style = ArdourCanvas::Ruler::Mark::Major;
                                snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
                        } else {
                                snprintf (buf, sizeof(buf)," ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
-
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                        }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = pos;
-
+                       mark.label = buf;
+                       mark.position = pos;
+                       marks.push_back (mark);
                        Timecode::increment_hours( timecode, _session->config.get_subframes_per_frame() );
                }
-         break;
-       case timecode_show_frames:
+               break;
+       case timecode_show_many_hours:
                // Find timecode time of this sample (pos)
                _session->sample_to_timecode(pos, timecode, true /* use_offset */, false /* use_subframes */ );
-               // Go to next whole frame down
-               Timecode::frames_floor( timecode );
+               // Go to next whole hour down
+               Timecode::hours_floor (timecode);
 
-               for (n = 0; n < timecode_nmarks; n++) {
+               for (n = 0; n < timecode_nmarks; ) {
                        _session->timecode_to_sample(timecode, pos, true /* use_offset */, false /* use_subframes */ );
-                       if ((timecode.frames % timecode_mark_modulo) == 0)  {
-                               if (timecode.frames == 0) {
-                                 (*marks)[n].style = GtkCustomRulerMarkMajor;
-                               } else {
-                                 (*marks)[n].style = GtkCustomRulerMarkMinor;
-                               }
-                               (*marks)[n].position = pos;
+                       if ((timecode.hours % timecode_mark_modulo) == 0) {
+                               mark.style = ArdourCanvas::Ruler::Mark::Major;
                                snprintf (buf, sizeof(buf), "%s%02u:%02u:%02u:%02u", timecode.negative ? "-" : "", timecode.hours, timecode.minutes, timecode.seconds, timecode.frames);
-                       } else {
-                               snprintf (buf, sizeof(buf)," ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
-                               (*marks)[n].position = pos;
-
-                       }
-                       (*marks)[n].label = g_strdup (buf);
-                       Timecode::increment( timecode, _session->config.get_subframes_per_frame() );
+                                mark.label = buf;
+                                mark.position = pos;
+                                marks.push_back (mark);
+                                ++n;
+                        }
+                        /* can't use Timecode::increment_hours() here because we may be traversing thousands of hours
+                           and doing it 1 hour at a time is just stupid (and slow).
+                        */
+                        timecode.hours += timecode_mark_modulo;
                }
-
-         break;
+               break;
        }
-
-       return timecode_nmarks;
 }
 
-
 void
-Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
-                                ARDOUR::TempoMap::BBTPointList::const_iterator begin,
-                                ARDOUR::TempoMap::BBTPointList::const_iterator end)
+Editor::compute_bbt_ruler_scale (std::vector<ARDOUR::TempoMap::BBTPoint>& grid, framepos_t lower, framepos_t upper)
 {
        if (_session == 0) {
                return;
        }
 
-       TempoMap::BBTPointList::const_iterator i;
+       std::vector<TempoMap::BBTPoint>::const_iterator i;
        Timecode::BBT_Time lower_beat, upper_beat; // the beats at each end of the ruler
+       double floor_lower_beat = floor(_session->tempo_map().beat_at_frame (lower));
+
+       if (floor_lower_beat < 0.0) {
+               floor_lower_beat = 0.0;
+       }
 
-       _session->bbt_time (lower, lower_beat);
-       _session->bbt_time (upper, upper_beat);
+       const framecnt_t beat_before_lower_pos = _session->tempo_map().frame_at_beat (floor_lower_beat);
+       const framecnt_t beat_after_upper_pos = _session->tempo_map().frame_at_beat (floor (_session->tempo_map().beat_at_frame (upper)) + 1.0);
+
+       _session->bbt_time (beat_before_lower_pos, lower_beat);
+       _session->bbt_time (beat_after_upper_pos, upper_beat);
        uint32_t beats = 0;
 
        bbt_accent_modulo = 1;
@@ -1232,7 +1035,7 @@ Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
        bbt_bars = 0;
        bbt_nmarks = 1;
 
-       bbt_ruler_scale =  bbt_over;
+       bbt_ruler_scale =  bbt_show_many;
 
        switch (_snap_type) {
        case SnapToBeatDiv2:
@@ -1304,115 +1107,122 @@ Editor::compute_bbt_ruler_scale (framepos_t lower, framepos_t upper,
                 bbt_beat_subdivision = 4;
                break;
        }
-
-       if (distance (begin, end) == 0) {
+       if (distance (grid.begin(), grid.end()) == 0) {
                return;
        }
 
-       i = end;
+       i = grid.end();
        i--;
-       if ((*i).beat >= (*begin).beat) {
-               bbt_bars = (*i).bar - (*begin).bar;
+
+       /* XX ?? */
+       if ((*i).beat >= (*grid.begin()).beat) {
+               bbt_bars = (*i).bar - (*grid.begin()).bar;
        } else {
-               bbt_bars = (*i).bar - (*begin).bar - 1;
+               bbt_bars = (*i).bar - (*grid.begin()).bar;
        }
-       beats = distance (begin, end) - bbt_bars;
 
+       beats = distance (grid.begin(), grid.end()) - bbt_bars;
+       double beat_density = ((distance (grid.begin(), grid.end()) + 1) * ((double) (upper - lower) / (double) (1 + grid.back().frame - grid.front().frame))) / 5.0;
        /* Only show the bar helper if there aren't many bars on the screen */
        if ((bbt_bars < 2) || (beats < 5)) {
                bbt_bar_helper_on = true;
        }
 
-       if (bbt_bars > 8192) {
-               bbt_ruler_scale =  bbt_over;
-       } else if (bbt_bars > 1024) {
+       if (beat_density > 8192) {
+               bbt_ruler_scale = bbt_show_many;
+       } else if (beat_density > 1024) {
                bbt_ruler_scale = bbt_show_64;
-       } else if (bbt_bars > 256) {
+       } else if (beat_density > 512) {
                bbt_ruler_scale = bbt_show_16;
-       } else if (bbt_bars > 64) {
+       } else if (beat_density > 128) {
                bbt_ruler_scale = bbt_show_4;
-       } else if (bbt_bars > 10) {
+       } else if (beat_density > 16) {
                bbt_ruler_scale =  bbt_show_1;
-       } else if (bbt_bars > 2) {
+       } else if (beat_density > 2) {
                bbt_ruler_scale =  bbt_show_beats;
-       } else  if (bbt_bars > 0) {
+       } else  if (beat_density > 0.5) {
                bbt_ruler_scale =  bbt_show_ticks;
        } else {
                bbt_ruler_scale =  bbt_show_ticks_detail;
        }
 
-       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)) {
+       if ((bbt_ruler_scale == bbt_show_ticks_detail) && beats < 3) {
                bbt_ruler_scale =  bbt_show_ticks_super_detail;
        }
 }
 
-gint
-Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper, gint /*maxchars*/)
+static void
+edit_last_mark_label (std::vector<ArdourCanvas::Ruler::Mark>& marks, const std::string& newlabel)
+{
+       ArdourCanvas::Ruler::Mark copy = marks.back();
+       copy.label = newlabel;
+       marks.pop_back ();
+       marks.push_back (copy);
+}
+
+void
+Editor::metric_get_bbt (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
 {
        if (_session == 0) {
-               return 0;
+               return;
        }
 
-       TempoMap::BBTPointList::const_iterator i;
+       std::vector<TempoMap::BBTPoint>::const_iterator i;
 
        char buf[64];
        gint  n = 0;
        framepos_t pos;
        Timecode::BBT_Time next_beat;
-       framepos_t next_beat_pos;
        uint32_t beats = 0;
-
        uint32_t tick = 0;
        uint32_t skip;
        uint32_t t;
-       framepos_t frame_skip;
-       double frame_skip_error;
        double bbt_position_of_helper;
-       double accumulated_error;
        bool i_am_accented = false;
        bool helper_active = false;
+       ArdourCanvas::Ruler::Mark mark;
 
-       ARDOUR::TempoMap::BBTPointList::const_iterator begin;
-       ARDOUR::TempoMap::BBTPointList::const_iterator end;
+       std::vector<TempoMap::BBTPoint> grid;
 
-       compute_current_bbt_points (lower, upper, begin, end);
+       compute_current_bbt_points (grid, lower, upper);
 
-       if (distance (begin, end) == 0) {
-               return 0;
+       if (distance (grid.begin(), grid.end()) == 0) {
+               return;
        }
 
        switch (bbt_ruler_scale) {
 
        case bbt_show_beats:
-               beats = distance (begin, end);
+
+               beats = distance (grid.begin(), grid.end());
                bbt_nmarks = beats + 2;
 
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
+               mark.label = "";
+               mark.position = lower;
+               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+               marks.push_back (mark);
 
-               (*marks)[0].label = g_strdup(" ");
-               (*marks)[0].position = lower;
-               (*marks)[0].style = GtkCustomRulerMarkMicro;
-               
-               for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
+               for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
-                               (*marks)[0].label = g_strdup (buf);
+                               edit_last_mark_label (marks, buf);
                                helper_active = true;
                        } else {
 
                                if ((*i).is_bar()) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
                                } else if (((*i).beat % 2 == 1)) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
-                                       snprintf (buf, sizeof(buf), " ");
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
+                                       buf[0] = '\0';
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMicro;
-                                       snprintf (buf, sizeof(buf), " ");
+                                       mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                                       buf[0] = '\0';
                                }
-                               (*marks)[n].label =  g_strdup (buf);
-                               (*marks)[n].position = (*i).frame;
+                               mark.label = buf;
+                               mark.position = (*i).frame;
+                               marks.push_back (mark);
                                n++;
                        }
                }
@@ -1420,89 +1230,70 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
 
        case bbt_show_ticks:
 
-               beats = distance (begin, end);
+               beats = distance (grid.begin(), grid.end());
                bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
 
                bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
 
-               (*marks)[0].label = g_strdup(" ");
-               (*marks)[0].position = lower;
-               (*marks)[0].style = GtkCustomRulerMarkMicro;
+               // could do marks.assign() here to preallocate
 
-               for (n = 1, i = begin; n < bbt_nmarks && i != end; ++i) {
+               mark.label = "";
+               mark.position = lower;
+               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+               marks.push_back (mark);
+
+               for (n = 1, i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
-                               (*marks)[0].label = g_strdup (buf);
+                               edit_last_mark_label (marks, buf);
                                helper_active = true;
                        } else {
 
                                if ((*i).is_bar()) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
                                }
                                if (((*i).frame < bbt_position_of_helper) && helper_active) {
-                                       snprintf (buf, sizeof(buf), " ");
+                                       buf[0] = '\0';
                                }
-                               (*marks)[n].label =  g_strdup (buf);
-                               (*marks)[n].position = (*i).frame;
+                               mark.label =  buf;
+                               mark.position = (*i).frame;
+                               marks.push_back (mark);
                                n++;
                        }
 
                        /* Add the tick marks */
+                       skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
+                       tick = skip; // the first non-beat tick
+                       t = 0;
+                       while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
 
-                       /* Find the next beat */
-                       next_beat.beats = (*i).beat;
-                       next_beat.bars = (*i).bar;
-                       next_beat.ticks = 0;
-
-                       if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
-                                 next_beat.beats += 1;
-                       } else {
-                                 next_beat.bars += 1;
-                                 next_beat.beats = 1;
-                       }
-
-                       next_beat_pos = _session->tempo_map().frame_time(next_beat);
-
-                       frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
-                       frame_skip_error -= frame_skip;
-                       skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
-
-                       pos = (*i).frame + frame_skip;
-                       accumulated_error = frame_skip_error;
+                               next_beat.beats = (*i).beat;
+                               next_beat.bars = (*i).bar;
+                               next_beat.ticks = tick;
+                               pos = _session->tempo_map().frame_at_bbt (next_beat);
 
-                       tick = skip;
-
-                       for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
-
-                               if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
+                               if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
                                        i_am_accented = true;
                                }
-
-                               snprintf (buf, sizeof(buf), " ");
-                               (*marks)[n].label = g_strdup (buf);
-
-                               /* Error compensation for float to framepos_t*/
-                               accumulated_error += frame_skip_error;
-                               if (accumulated_error > 1) {
-                                       pos += 1;
-                                       accumulated_error -= 1.0f;
-                               }
-
-                               (*marks)[n].position = pos;
+                               mark.label = "";
+                               mark.position = pos;
 
                                if ((bbt_beat_subdivision > 4) && i_am_accented) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                }
                                i_am_accented = false;
-                               n++;
+                               marks.push_back (mark);
+
+                               tick += skip;
+                               ++t;
+                               ++n;
                        }
                }
 
@@ -1510,94 +1301,75 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
 
        case bbt_show_ticks_detail:
 
-               beats = distance (begin, end);
+               beats = distance (grid.begin(), grid.end());
                bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
 
-               bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
+               bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
 
-               (*marks)[0].label = g_strdup(" ");
-               (*marks)[0].position = lower;
-               (*marks)[0].style = GtkCustomRulerMarkMicro;
+               mark.label = "";
+               mark.position = lower;
+               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+               marks.push_back (mark);
 
-               for (n = 1,   i = begin; n < bbt_nmarks && i != end; ++i) {
+               for (n = 1,   i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
-                               (*marks)[0].label = g_strdup (buf);
+                               edit_last_mark_label (marks, buf);
                                helper_active = true;
                        } else {
 
                                if ((*i).is_bar()) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
                                }
                                if (((*i).frame < bbt_position_of_helper) && helper_active) {
-                                       snprintf (buf, sizeof(buf), " ");
+                                       buf[0] = '\0';
                                }
-                               (*marks)[n].label =  g_strdup (buf);
-                               (*marks)[n].position = (*i).frame;
+                               mark.label =  buf;
+                               mark.position = (*i).frame;
+                               marks.push_back (mark);
                                n++;
                        }
 
                        /* Add the tick marks */
+                       skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
+                       tick = skip; // the first non-beat tick
 
-                       /* Find the next beat */
+                       t = 0;
+                       while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
 
-                       next_beat.beats = (*i).beat;
-                       next_beat.bars = (*i).bar;
+                               next_beat.beats = (*i).beat;
+                               next_beat.bars = (*i).bar;
+                               next_beat.ticks = tick;
+                               pos = _session->tempo_map().frame_at_bbt (next_beat);
 
-                       if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
-                                 next_beat.beats += 1;
-                       } else {
-                                 next_beat.bars += 1;
-                                 next_beat.beats = 1;
-                       }
-
-                       next_beat_pos = _session->tempo_map().frame_time(next_beat);
-
-                       frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
-                       frame_skip_error -= frame_skip;
-                       skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
-
-                       pos = (*i).frame + frame_skip;
-                       accumulated_error = frame_skip_error;
-
-                       tick = skip;
-
-                       for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
-
-                               if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
-                                       i_am_accented = true;
+                               if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
+                                       i_am_accented = true;
                                }
-
                                if (i_am_accented && (pos > bbt_position_of_helper)){
                                        snprintf (buf, sizeof(buf), "%" PRIu32, tick);
                                } else {
-                                       snprintf (buf, sizeof(buf), " ");
-                               }
-
-                               (*marks)[n].label = g_strdup (buf);
-
-                               /* Error compensation for float to framepos_t*/
-                               accumulated_error += frame_skip_error;
-                               if (accumulated_error > 1) {
-                                       pos += 1;
-                                       accumulated_error -= 1.0f;
+                                       buf[0] = '\0';
                                }
 
-                               (*marks)[n].position = pos;
+                               mark.label = buf;
+                               mark.position = pos;
 
                                if ((bbt_beat_subdivision > 4) && i_am_accented) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                }
                                i_am_accented = false;
-                               n++;
+                               marks.push_back (mark);
+
+                               tick += skip;
+                               ++t;
+                               ++n;
                        }
                }
 
@@ -1605,130 +1377,109 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
 
        case bbt_show_ticks_super_detail:
 
-               beats = distance (begin, end);
+               beats = distance (grid.begin(), grid.end());
                bbt_nmarks = (beats + 2) * bbt_beat_subdivision;
 
-               bbt_position_of_helper = lower + (30 * Editor::get_current_zoom ());
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
+               bbt_position_of_helper = lower + (3 * Editor::get_current_zoom ());
 
-               (*marks)[0].label = g_strdup(" ");
-               (*marks)[0].position = lower;
-               (*marks)[0].style = GtkCustomRulerMarkMicro;
+               mark.label = "";
+               mark.position = lower;
+               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+               marks.push_back (mark);
 
-               for (n = 1,   i = begin; n < bbt_nmarks && i != end; ++i) {
+               for (n = 1,   i = grid.begin(); n < bbt_nmarks && i != grid.end(); ++i) {
 
                        if ((*i).frame < lower && (bbt_bar_helper_on)) {
                                  snprintf (buf, sizeof(buf), "<%" PRIu32 "|%" PRIu32, (*i).bar, (*i).beat);
-                                 (*marks)[0].label = g_strdup (buf);
+                                 edit_last_mark_label (marks, buf);
                                  helper_active = true;
                        } else {
 
                                  if ((*i).is_bar()) {
-                                         (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                         mark.style = ArdourCanvas::Ruler::Mark::Major;
                                          snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
                                  } else {
-                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                          snprintf (buf, sizeof(buf), "%" PRIu32, (*i).beat);
                                  }
                                  if (((*i).frame < bbt_position_of_helper) && helper_active) {
-                                         snprintf (buf, sizeof(buf), " ");
+                                         buf[0] = '\0';
                                  }
-                                 (*marks)[n].label =  g_strdup (buf);
-                                 (*marks)[n].position = (*i).frame;
+                                 mark.label =  buf;
+                                 mark.position = (*i).frame;
+                                 marks.push_back (mark);
                                  n++;
                        }
 
                        /* Add the tick marks */
-
-                       /* Find the next beat */
+                       skip = Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision;
 
                        next_beat.beats = (*i).beat;
                        next_beat.bars = (*i).bar;
+                       tick = skip; // the first non-beat tick
+                       t = 0;
+                       while (tick < Timecode::BBT_Time::ticks_per_beat && (n < bbt_nmarks)) {
 
-                       if ((*i).meter->divisions_per_bar() > (next_beat.beats + 1)) {
-                                 next_beat.beats += 1;
-                       } else {
-                                 next_beat.bars += 1;
-                                 next_beat.beats = 1;
-                       }
-
-                       next_beat_pos = _session->tempo_map().frame_time(next_beat);
-
-                       frame_skip = (framepos_t) floor (frame_skip_error = (_session->frame_rate() *  60) / (bbt_beat_subdivision * (*i).tempo->beats_per_minute()));
-                       frame_skip_error -= frame_skip;
-                       skip = (uint32_t) (Timecode::BBT_Time::ticks_per_beat / bbt_beat_subdivision);
-
-                       pos = (*i).frame + frame_skip;
-                       accumulated_error = frame_skip_error;
-
-                       tick = skip;
-
-                       for (t = 0; (tick < Timecode::BBT_Time::ticks_per_beat) && (n < bbt_nmarks) && (pos < next_beat_pos) ; pos += frame_skip, tick += skip, ++t) {
-
-                                 if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
-                                         i_am_accented = true;
-                                 }
-
-                                 if (pos > bbt_position_of_helper) {
-                                         snprintf (buf, sizeof(buf), "%" PRIu32, tick);
-                                 } else {
-                                         snprintf (buf, sizeof(buf), " ");
-                                 }
+                               next_beat.ticks = tick;
+                               pos = _session->tempo_map().frame_at_bbt (next_beat);
+                               if (t % bbt_accent_modulo == (bbt_accent_modulo - 1)) {
+                                       i_am_accented = true;
+                               }
 
-                                 (*marks)[n].label = g_strdup (buf);
+                               if (pos > bbt_position_of_helper) {
+                                       snprintf (buf, sizeof(buf), "%" PRIu32, tick);
+                               } else {
+                                       buf[0] = '\0';
+                               }
 
-                                 /* Error compensation for float to framepos_t*/
-                                 accumulated_error += frame_skip_error;
-                                 if (accumulated_error > 1) {
-                                         pos += 1;
-                                         accumulated_error -= 1.0f;
-                                 }
+                               mark.label = buf;
+                               mark.position = pos;
 
-                                 (*marks)[n].position = pos;
+                               if ((bbt_beat_subdivision > 4) && i_am_accented) {
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
+                               } else {
+                                       mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                               }
+                               i_am_accented = false;
+                               marks.push_back (mark);
 
-                                 if ((bbt_beat_subdivision > 4) && i_am_accented) {
-                                         (*marks)[n].style = GtkCustomRulerMarkMinor;
-                                 } else {
-                                         (*marks)[n].style = GtkCustomRulerMarkMicro;
-                                 }
-                                 i_am_accented = false;
-                                 n++;
+                               tick += skip;
+                               ++t;
+                               ++n;
                        }
                }
 
          break;
 
-       case bbt_over:
-                       bbt_nmarks = 1;
-                       *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
-                       snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
-                       (*marks)[0].style = GtkCustomRulerMarkMajor;
-                       (*marks)[0].label = g_strdup (buf);
-                       (*marks)[0].position = lower;
-                       n = 1;
-
-         break;
+        case bbt_show_many:
+                bbt_nmarks = 1;
+                snprintf (buf, sizeof(buf), "cannot handle %" PRIu32 " bars", bbt_bars );
+                mark.style = ArdourCanvas::Ruler::Mark::Major;
+                mark.label = buf;
+                mark.position = lower;
+                marks.push_back (mark);
+                break;
 
        case bbt_show_64:
                        bbt_nmarks = (gint) (bbt_bars / 64) + 1;
-                       *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
-                       for (n = 0,   i = begin; i != end && n < bbt_nmarks; i++) {
+                       for (n = 0,   i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
                                if ((*i).is_bar()) {
                                        if ((*i).bar % 64 == 1) {
                                                if ((*i).bar % 256 == 1) {
                                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
-                                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                                } else {
-                                                       snprintf (buf, sizeof(buf), " ");
+                                                       buf[0] = '\0';
                                                        if ((*i).bar % 256 == 129)  {
-                                                               (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                                               mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                                        } else {
-                                                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                                        }
                                                }
-                                               (*marks)[n].label = g_strdup (buf);
-                                               (*marks)[n].position = (*i).frame;
-                                               n++;
+                                               mark.label = buf;
+                                               mark.position = (*i).frame;
+                                               marks.push_back (mark);
+                                               ++n;
                                        }
                                }
                        }
@@ -1736,24 +1487,24 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
 
        case bbt_show_16:
                        bbt_nmarks = (bbt_bars / 16) + 1;
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
-               for (n = 0,  i = begin; i != end && n < bbt_nmarks; i++) {
+               for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; i++) {
                        if ((*i).is_bar()) {
                          if ((*i).bar % 16 == 1) {
                                if ((*i).bar % 64 == 1) {
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                } else {
-                                       snprintf (buf, sizeof(buf), " ");
+                                       buf[0] = '\0';
                                        if ((*i).bar % 64 == 33)  {
-                                               (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                               mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                        } else {
-                                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                        }
                                }
-                               (*marks)[n].label = g_strdup (buf);
-                               (*marks)[n].position = (*i).frame;
-                               n++;
+                               mark.label = buf;
+                               mark.position = (*i).frame;
+                               marks.push_back (mark);
+                               ++n;
                          }
                        }
                }
@@ -1761,58 +1512,54 @@ Editor::metric_get_bbt (GtkCustomRulerMark **marks, gdouble lower, gdouble upper
 
        case bbt_show_4:
                bbt_nmarks = (bbt_bars / 4) + 1;
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks);
-               for (n = 0,   i = begin; i != end && n < bbt_nmarks; ++i) {
+               for (n = 0,   i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
                        if ((*i).is_bar()) {
                          if ((*i).bar % 4 == 1) {
                                if ((*i).bar % 16 == 1) {
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                } else {
-                                       snprintf (buf, sizeof(buf), " ");
+                                       buf[0] = '\0';
                                        if ((*i).bar % 16 == 9)  {
-                                               (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                               mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                        } else {
-                                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                                        }
                                }
-                               (*marks)[n].label = g_strdup (buf);
-                               (*marks)[n].position = (*i).frame;
-                               n++;
+                               mark.label = buf;
+                               mark.position = (*i).frame;
+                               marks.push_back (mark);
+                               ++n;
                          }
                        }
                }
          break;
 
        case bbt_show_1:
-  //   default:
+//     default:
                bbt_nmarks = bbt_bars + 2;
-               *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * bbt_nmarks );
-               for (n = 0,  i = begin; i != end && n < bbt_nmarks; i++) {
+               for (n = 0,  i = grid.begin(); i != grid.end() && n < bbt_nmarks; ++i) {
                        if ((*i).is_bar()) {
                          if ((*i).bar % 4 == 1) {
                                        snprintf (buf, sizeof(buf), "%" PRIu32, (*i).bar);
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                          } else {
-                               snprintf (buf, sizeof(buf), " ");
-                               if ((*i).bar % 4 == 3)  {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
-                               } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMicro;
-                               }
+                                 buf[0] = '\0';
+                                 if ((*i).bar % 4 == 3)  {
+                                         mark.style = ArdourCanvas::Ruler::Mark::Minor;
+                                 } else {
+                                         mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                                 }
                          }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = (*i).frame;
-                       n++;
+                         mark.label = buf;
+                         mark.position = (*i).frame;
+                         marks.push_back (mark);
+                         ++n;
                        }
                }
-
-       break;
+               break;
 
        }
-
-       return n; //return the actual number of marks made, since we might have skipped some from fractional time signatures
-
 }
 
 void
@@ -1821,29 +1568,28 @@ Editor::set_samples_ruler_scale (framepos_t lower, framepos_t upper)
        _samples_ruler_interval = (upper - lower) / 5;
 }
 
-gint
-Editor::metric_get_samples (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
+void
+Editor::metric_get_samples (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
 {
        framepos_t pos;
        framepos_t const ilower = (framepos_t) floor (lower);
        gchar buf[16];
        gint nmarks;
        gint n;
+       ArdourCanvas::Ruler::Mark mark;
 
        if (_session == 0) {
-               return 0;
+               return;
        }
 
        nmarks = 5;
-       *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * nmarks);
        for (n = 0, pos = ilower; n < nmarks; pos += _samples_ruler_interval, ++n) {
                snprintf (buf, sizeof(buf), "%" PRIi64, pos);
-               (*marks)[n].label = g_strdup (buf);
-               (*marks)[n].position = pos;
-               (*marks)[n].style = GtkCustomRulerMarkMajor;
+               mark.label = buf;
+               mark.position = pos;
+               mark.style = ArdourCanvas::Ruler::Mark::Major;
+               marks.push_back (mark);
        }
-
-       return nmarks;
 }
 
 static void
@@ -1862,13 +1608,13 @@ sample_to_clock_parts ( framepos_t sample,
        long millisecs;
 
        left = sample;
-       hrs = left / (sample_rate * 60 * 60);
-       left -= hrs * sample_rate * 60 * 60;
-       mins = left / (sample_rate * 60);
-       left -= mins * sample_rate * 60;
-       secs = left / sample_rate;
-       left -= secs * sample_rate;
-       millisecs = left * 1000 / sample_rate;
+       hrs = left / (sample_rate * 60 * 60 * 1000);
+       left -= hrs * sample_rate * 60 * 60 * 1000;
+       mins = left / (sample_rate * 60 * 1000);
+       left -= mins * sample_rate * 60 * 1000;
+       secs = left / (sample_rate * 1000);
+       left -= secs * sample_rate * 1000;
+       millisecs = left / sample_rate;
 
        *millisecs_p = millisecs;
        *secs_p = secs;
@@ -1881,14 +1627,13 @@ sample_to_clock_parts ( framepos_t sample,
 void
 Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
 {
-       framepos_t fr;
+       framepos_t fr = _session->frame_rate() * 1000;
        framepos_t spacer;
 
        if (_session == 0) {
                return;
        }
 
-       fr = _session->frame_rate();
 
        /* to prevent 'flashing' */
        if (lower > (spacer = (framepos_t)(128 * Editor::get_current_zoom ()))) {
@@ -1897,96 +1642,122 @@ Editor::set_minsec_ruler_scale (framepos_t lower, framepos_t upper)
                lower = 0;
        }
        upper += spacer;
-       framecnt_t const range = upper - lower;
+       framecnt_t const range = (upper - lower) * 1000;
 
-       if (range <  (fr / 50)) {
-               minsec_mark_interval =  fr / 1000; /* show 1/1000 seconds */
-               minsec_ruler_scale = minsec_show_frames;
-               minsec_mark_modulo = 10;
-       } else if (range <= (fr / 10)) { /* 0-0.1 second */
+        if (range <= (fr / 10)) { /* 0-0.1 second */
                minsec_mark_interval = fr / 1000; /* show 1/1000 seconds */
-               minsec_ruler_scale = minsec_show_frames;
+               minsec_ruler_scale = minsec_show_msecs;
                minsec_mark_modulo = 10;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
        } else if (range <= (fr / 2)) { /* 0-0.5 second */
                minsec_mark_interval = fr / 100;  /* show 1/100 seconds */
-               minsec_ruler_scale = minsec_show_frames;
+               minsec_ruler_scale = minsec_show_msecs;
                minsec_mark_modulo = 100;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
        } else if (range <= fr) { /* 0-1 second */
                minsec_mark_interval = fr / 10;  /* show 1/10 seconds */
-               minsec_ruler_scale = minsec_show_frames;
+               minsec_ruler_scale = minsec_show_msecs;
                minsec_mark_modulo = 200;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
        } else if (range <= 2 * fr) { /* 1-2 seconds */
                minsec_mark_interval = fr / 10; /* show 1/10 seconds */
-               minsec_ruler_scale = minsec_show_frames;
+               minsec_ruler_scale = minsec_show_msecs;
                minsec_mark_modulo = 500;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
        } else if (range <= 8 * fr) { /* 2-5 seconds */
                minsec_mark_interval =  fr / 5; /* show 2 seconds */
-               minsec_ruler_scale = minsec_show_frames;
+               minsec_ruler_scale = minsec_show_msecs;
                minsec_mark_modulo = 1000;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
        } else if (range <= 16 * fr) { /* 8-16 seconds */
                minsec_mark_interval =  fr; /* show 1 seconds */
                minsec_ruler_scale = minsec_show_seconds;
                minsec_mark_modulo = 2;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
        } else if (range <= 30 * fr) { /* 10-30 seconds */
                minsec_mark_interval =  fr; /* show 1 seconds */
                minsec_ruler_scale = minsec_show_seconds;
                 minsec_mark_modulo = 5;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
        } else if (range <= 60 * fr) { /* 30-60 seconds */
                 minsec_mark_interval = fr; /* show 1 seconds */
                 minsec_ruler_scale = minsec_show_seconds;
                 minsec_mark_modulo = 5;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
         } else if (range <= 2 * 60 * fr) { /* 1-2 minutes */
                 minsec_mark_interval = 5 * fr; /* show 5 seconds */
                 minsec_ruler_scale = minsec_show_seconds;
                 minsec_mark_modulo = 3;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
         } else if (range <= 4 * 60 * fr) { /* 4 minutes */
                 minsec_mark_interval = 5 * fr; /* show 10 seconds */
                 minsec_ruler_scale = minsec_show_seconds;
                 minsec_mark_modulo = 30;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
         } else if (range <= 10 * 60 * fr) { /* 10 minutes */
                 minsec_mark_interval = 30 * fr; /* show 30 seconds */
                 minsec_ruler_scale = minsec_show_seconds;
                 minsec_mark_modulo = 120;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
         } else if (range <= 30 * 60 * fr) { /* 10-30 minutes */
                 minsec_mark_interval =  60 * fr; /* show 1 minute */
                 minsec_ruler_scale = minsec_show_minutes;
                minsec_mark_modulo = 5;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
         } else if (range <= 60 * 60 * fr) { /* 30 minutes - 1hr */
                 minsec_mark_interval = 2 * 60 * fr; /* show 2 minutes */
                 minsec_ruler_scale = minsec_show_minutes;
                 minsec_mark_modulo = 10;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
         } else if (range <= 4 * 60 * 60 * fr) { /* 1 - 4 hrs*/
                 minsec_mark_interval = 5 * 60 * fr; /* show 10 minutes */
                 minsec_ruler_scale = minsec_show_minutes;
                 minsec_mark_modulo = 30;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
         } else if (range <= 8 * 60 * 60 * fr) { /* 4 - 8 hrs*/
                 minsec_mark_interval = 20 * 60 * fr; /* show 20 minutes */
                 minsec_ruler_scale = minsec_show_minutes;
                 minsec_mark_modulo = 60;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
         } else if (range <= 16 * 60 * 60 * fr) { /* 16-24 hrs*/
                 minsec_mark_interval =  60 * 60 * fr; /* show 60 minutes */
                 minsec_ruler_scale = minsec_show_hours;
                minsec_mark_modulo = 2;
+                minsec_nmarks = 2 + (range / minsec_mark_interval);
         } else {
 
-                /* not possible if framepos_t is a 32 bit quantity */
+                const framecnt_t hours_in_range = range / (60 * 60 * fr);
+                const int text_width_rough_guess = 70; /* pixels, very very approximate guess at how wide the tick mark text is */
+
+                /* Normally we do not need to know anything about the width of the canvas
+                   to set the ruler scale, because the caller has already determined
+                   the width and set lower + upper arguments to this function to match that.
+
+                   But in this case, where the range defined by lower and uppper can vary
+                   substantially (anything from 24hrs+ to several billion years)
+                   trying to decide which tick marks to show does require us to know
+                   about the available width.
+                */
 
-                minsec_mark_interval = 4 * 60 * 60 * fr; /* show 4 hrs */
+                minsec_nmarks = _track_canvas->width() / text_width_rough_guess;
+                minsec_mark_modulo = max ((framecnt_t) 1, 1 + (hours_in_range / minsec_nmarks));
+                minsec_mark_interval = minsec_mark_modulo * (60 * 60 * fr);
+                minsec_ruler_scale = minsec_show_many_hours;
         }
-       minsec_nmarks = 2 + (range / minsec_mark_interval);
 }
 
-gint
-Editor::metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble /*upper*/, gint /*maxchars*/)
+void
+Editor::metric_get_minsec (std::vector<ArdourCanvas::Ruler::Mark>& marks, gdouble lower, gdouble upper, gint /*maxchars*/)
 {
        framepos_t pos;
        framepos_t spacer;
        long hrs, mins, secs, millisecs;
        gchar buf[16];
        gint n;
+       ArdourCanvas::Ruler::Mark mark;
 
        if (_session == 0) {
-               return 0;
+               return;
        }
 
        /* to prevent 'flashing' */
@@ -1996,78 +1767,99 @@ Editor::metric_get_minsec (GtkCustomRulerMark **marks, gdouble lower, gdouble /*
                lower = 0;
        }
 
-       *marks = (GtkCustomRulerMark *) g_malloc (sizeof(GtkCustomRulerMark) * minsec_nmarks);
-       pos = ((((framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
+       pos = (((1000 * (framepos_t) floor(lower)) + (minsec_mark_interval/2))/minsec_mark_interval) * minsec_mark_interval;
+
        switch (minsec_ruler_scale) {
+
+       case minsec_show_msecs:
+               for (n = 0; n < minsec_nmarks && n < upper; pos += minsec_mark_interval, ++n) {
+                       sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
+                       if (millisecs % minsec_mark_modulo == 0) {
+                               if (millisecs == 0) {
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
+                               } else {
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
+                               }
+                               snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
+                       } else {
+                               buf[0] = '\0';
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                       }
+                       mark.label = buf;
+                       mark.position = pos/1000.0;
+                       marks.push_back (mark);
+               }
+                break;
+
        case minsec_show_seconds:
                for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
                        sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
                        if (secs % minsec_mark_modulo == 0) {
                                if (secs == 0) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Major;
                                } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                       mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                }
-                               snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
+                               snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
                        } else {
-                               snprintf (buf, sizeof(buf), " ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
+                               buf[0] = '\0';
+                               mark.style = ArdourCanvas::Ruler::Mark::Micro;
                        }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = pos;
+                       mark.label = buf;
+                       mark.position = pos/1000.0;
+                       marks.push_back (mark);
                }
-         break;
+                break;
+
        case minsec_show_minutes:
                for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
                         if (mins % minsec_mark_modulo == 0) {
                                 if (mins == 0) {
-                                        (*marks)[n].style = GtkCustomRulerMarkMajor;
+                                        mark.style = ArdourCanvas::Ruler::Mark::Major;
                                 } else {
-                                        (*marks)[n].style = GtkCustomRulerMarkMinor;
+                                        mark.style = ArdourCanvas::Ruler::Mark::Minor;
                                 }
-                               snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
+                                snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld", hrs, mins, secs);
                         } else {
-                                snprintf (buf, sizeof(buf), " ");
-                                (*marks)[n].style = GtkCustomRulerMarkMicro;
+                               buf[0] = '\0';
+                                mark.style = ArdourCanvas::Ruler::Mark::Micro;
                         }
-                        (*marks)[n].label = g_strdup (buf);
-                        (*marks)[n].position = pos;
+                        mark.label = buf;
+                        mark.position = pos/1000.0;
+                       marks.push_back (mark);
                 }
-         break;
+                break;
+
        case minsec_show_hours:
                 for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
                         sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
                         if (hrs % minsec_mark_modulo == 0) {
-                                (*marks)[n].style = GtkCustomRulerMarkMajor;
-                                snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
+                                mark.style = ArdourCanvas::Ruler::Mark::Major;
+                                snprintf (buf, sizeof(buf), "%02ld:%02ld", hrs, mins);
                         } else {
-                                snprintf (buf, sizeof(buf), " ");
-                                (*marks)[n].style = GtkCustomRulerMarkMicro;
+                               buf[0] = '\0';
+                                mark.style = ArdourCanvas::Ruler::Mark::Micro;
+                        }
+                        mark.label = buf;
+                        mark.position = pos/1000.0;
+                       marks.push_back (mark);
+                 }
+                 break;
+
+        case minsec_show_many_hours:
+                for (n = 0; n < minsec_nmarks; ) {
+                        sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
+                        if (hrs % minsec_mark_modulo == 0) {
+                                mark.style = ArdourCanvas::Ruler::Mark::Major;
+                                snprintf (buf, sizeof(buf), "%02ld:00", hrs);
+                                mark.label = buf;
+                                mark.position = pos/1000.0;
+                                marks.push_back (mark);
+                                ++n;
                         }
-                        (*marks)[n].label = g_strdup (buf);
-                        (*marks)[n].position = pos;
+                        pos += minsec_mark_interval;
                 }
-             break;
-       case minsec_show_frames:
-               for (n = 0; n < minsec_nmarks; pos += minsec_mark_interval, ++n) {
-                       sample_to_clock_parts (pos, _session->frame_rate(), &hrs, &mins, &secs, &millisecs);
-                       if (millisecs % minsec_mark_modulo == 0) {
-                               if (secs == 0) {
-                                       (*marks)[n].style = GtkCustomRulerMarkMajor;
-                               } else {
-                                       (*marks)[n].style = GtkCustomRulerMarkMinor;
-                               }
-                               snprintf (buf, sizeof(buf), "%02ld:%02ld:%02ld.%03ld", hrs, mins, secs, millisecs);
-                       } else {
-                               snprintf (buf, sizeof(buf), " ");
-                               (*marks)[n].style = GtkCustomRulerMarkMicro;
-                       }
-                       (*marks)[n].label = g_strdup (buf);
-                       (*marks)[n].position = pos;
-               }
-         break;
-       }
-
-       return minsec_nmarks;
+                break;
+        }
 }