+ auto sizer = new wxBoxSizer (wxHORIZONTAL);
+ sizer->Add (_labels_canvas, 0, wxEXPAND);
+ _labels_canvas->SetMinSize (wxSize (_labels_view->bbox().width, -1));
+ sizer->Add (_main_canvas, 1, wxEXPAND);
+ SetSizer (sizer);
+
+ _labels_canvas->Bind (wxEVT_PAINT, boost::bind (&Timeline::paint_labels, this));
+ _main_canvas->Bind (wxEVT_PAINT, boost::bind (&Timeline::paint_main, this));
+ _main_canvas->Bind (wxEVT_LEFT_DOWN, boost::bind (&Timeline::left_down, this, _1));
+ _main_canvas->Bind (wxEVT_LEFT_UP, boost::bind (&Timeline::left_up, this, _1));
+ _main_canvas->Bind (wxEVT_RIGHT_DOWN, boost::bind (&Timeline::right_down, this, _1));
+ _main_canvas->Bind (wxEVT_MOTION, boost::bind (&Timeline::mouse_moved, this, _1));
+ _main_canvas->Bind (wxEVT_SIZE, boost::bind (&Timeline::resized, this));
+ _main_canvas->Bind (wxEVT_MOUSEWHEEL, boost::bind(&Timeline::mouse_wheel_turned, this, _1));
+ _main_canvas->Bind (wxEVT_SCROLLWIN_TOP, boost::bind (&Timeline::scrolled, this, _1));
+ _main_canvas->Bind (wxEVT_SCROLLWIN_BOTTOM, boost::bind (&Timeline::scrolled, this, _1));
+ _main_canvas->Bind (wxEVT_SCROLLWIN_LINEUP, boost::bind (&Timeline::scrolled, this, _1));
+ _main_canvas->Bind (wxEVT_SCROLLWIN_LINEDOWN, boost::bind (&Timeline::scrolled, this, _1));
+ _main_canvas->Bind (wxEVT_SCROLLWIN_PAGEUP, boost::bind (&Timeline::scrolled, this, _1));
+ _main_canvas->Bind (wxEVT_SCROLLWIN_PAGEDOWN, boost::bind (&Timeline::scrolled, this, _1));
+ _main_canvas->Bind (wxEVT_SCROLLWIN_THUMBTRACK, boost::bind (&Timeline::scrolled, this, _1));
+
+ film_change(ChangeType::DONE, FilmProperty::CONTENT);
+
+ SetMinSize (wxSize (640, 4 * pixels_per_track() + 96));
+
+ _film_changed_connection = film->Change.connect (bind (&Timeline::film_change, this, _1, _2));
+ _film_content_change_connection = film->ContentChange.connect (bind (&Timeline::film_content_change, this, _1, _3, _4));
+
+ Bind (wxEVT_TIMER, boost::bind(&Timeline::update_playhead, this));
+ _timer.Start (200, wxTIMER_CONTINUOUS);
+
+ setup_scrollbars ();
+ _labels_canvas->ShowScrollbars (wxSHOW_SB_NEVER, wxSHOW_SB_NEVER);
+}
+
+
+void
+Timeline::mouse_wheel_turned(wxMouseEvent& event)
+{
+ auto const rotation = event.GetWheelRotation();
+
+ if (event.ControlDown()) {
+ /* On my mouse one click of the scroll wheel is 120, and it's -ve when
+ * scrolling the wheel towards me.
+ */
+ auto const scale = rotation > 0 ?
+ (1.0 / (rotation / 90.0)) :
+ (-rotation / 90.0);
+
+ int before_start_x;
+ int before_start_y;
+ _main_canvas->GetViewStart(&before_start_x, &before_start_y);
+
+ auto const before_pps = _pixels_per_second.get_value_or(1);
+ auto const before_pos = _last_mouse_wheel_x && *_last_mouse_wheel_x == event.GetX() ?
+ *_last_mouse_wheel_time :
+ (before_start_x * _x_scroll_rate + event.GetX()) / before_pps;
+
+ set_pixels_per_second(before_pps * scale);
+ setup_scrollbars();
+
+ auto after_left = std::max(0.0, before_pos * _pixels_per_second.get_value_or(1) - event.GetX());
+ _main_canvas->Scroll(after_left / _x_scroll_rate, before_start_y);
+ _labels_canvas->Scroll(0, before_start_y);
+ Refresh();
+
+ if (!_last_mouse_wheel_x || *_last_mouse_wheel_x != event.GetX()) {
+ _last_mouse_wheel_x = event.GetX();
+ _last_mouse_wheel_time = before_pos;
+ }
+ } else if (event.ShiftDown()) {
+ int before_start_x;
+ int before_start_y;
+ _main_canvas->GetViewStart(&before_start_x, &before_start_y);
+ auto const width = _main_canvas->GetSize().GetWidth();
+ _main_canvas->Scroll(std::max(0.0, before_start_x - rotation * 100.0 / width), before_start_y);
+ }
+}
+
+
+void
+Timeline::update_playhead ()
+{
+ Refresh ();
+}
+
+
+void
+Timeline::set_pixels_per_second (double pps)
+{
+ _pixels_per_second = max (_minimum_pixels_per_second, pps);
+}
+
+
+void
+Timeline::paint_labels ()
+{
+ wxPaintDC dc (_labels_canvas);
+
+ auto film = _film.lock();
+ if (film->content().empty()) {
+ return;
+ }