+ if (!cv->selected ()) {
+ clear_selection ();
+ cv->set_selected (true);
+ }
+
+ _menu.popup (selected_content (), ev.GetPosition ());
+}
+
+void
+Timeline::set_position_from_event (wxMouseEvent& ev)
+{
+ wxPoint const p = ev.GetPosition();
+
+ if (!_first_move) {
+ /* We haven't moved yet; in that case, we must move the mouse some reasonable distance
+ before the drag is considered to have started.
+ */
+ int const dist = sqrt (pow (p.x - _down_point.x, 2) + pow (p.y - _down_point.y, 2));
+ if (dist < 8) {
+ return;
+ }
+ _first_move = true;
+ }
+
+ if (!_down_view) {
+ return;
+ }
+
+ Time new_position = _down_view_position + (p.x - _down_point.x) / _pixels_per_time_unit;
+
+ if (_snap) {
+
+ bool first = true;
+ Time nearest_distance = TIME_MAX;
+ Time nearest_new_position = TIME_MAX;
+
+ /* Find the nearest content edge; this is inefficient */
+ for (ViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
+ shared_ptr<ContentView> cv = dynamic_pointer_cast<ContentView> (*i);
+ if (!cv || cv == _down_view) {
+ continue;
+ }
+
+ {
+ /* Snap starts to ends */
+ Time const d = abs (cv->content()->end() - new_position);
+ if (first || d < nearest_distance) {
+ nearest_distance = d;
+ nearest_new_position = cv->content()->end();
+ }
+ }
+
+ {
+ /* Snap ends to starts */
+ Time const d = abs (cv->content()->position() - (new_position + _down_view->content()->length_after_trim()));
+ if (d < nearest_distance) {
+ nearest_distance = d;
+ nearest_new_position = cv->content()->position() - _down_view->content()->length_after_trim ();
+ }
+ }
+
+ first = false;
+ }
+
+ if (!first) {
+ /* Snap if it's close; `close' means within a proportion of the time on the timeline */
+ if (nearest_distance < (width() / pixels_per_time_unit()) / 32) {
+ new_position = nearest_new_position;
+ }