+ ostringstream sstr;
+
+ TempoSection* prev = 0;
+ if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
+ _editor->tempo_curve_selected (tmap.previous_tempo_section (_tempo), true);
+ sstr << "end: " << fixed << setprecision(3) << tmap.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
+ }
+
+ if (_tempo->clamped()) {
+ _editor->tempo_curve_selected (_tempo, true);
+ sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
+ }
+
+ show_verbose_cursor_text (sstr.str());
+}
+
+void
+TempoEndDrag::setup_pointer_frame_offset ()
+{
+ TempoMap& map (_editor->session()->tempo_map());
+
+ _pointer_frame_offset = raw_grab_frame() - map.frame_at_quarter_note (_grab_qn);
+
+}
+
+void
+TempoEndDrag::motion (GdkEvent* event, bool first_move)
+{
+ TempoMap& map (_editor->session()->tempo_map());
+
+ if (first_move) {
+ _editor->begin_reversible_command (_("stretch end tempo"));
+ }
+
+
+
+ framepos_t const pf = adjusted_current_frame (event, false);
+ map.gui_stretch_tempo_end (&map.tempo_section_at_frame (_tempo->frame() - 1), map.frame_at_quarter_note (_grab_qn), pf);
+
+ ostringstream sstr;
+ sstr << "end: " << fixed << setprecision(3) << map.tempo_section_at_frame (_tempo->frame() - 1).end_note_types_per_minute() << "\n";
+
+ if (_tempo->clamped()) {
+ sstr << "start: " << fixed << setprecision(3) << _tempo->note_types_per_minute();
+ }
+
+ show_verbose_cursor_text (sstr.str());
+}
+
+void
+TempoEndDrag::finished (GdkEvent* event, bool movement_occurred)
+{
+ if (!movement_occurred) {
+ return;
+ }
+
+ TempoMap& tmap (_editor->session()->tempo_map());
+
+ XMLNode &after = tmap.get_state();
+ _editor->session()->add_command(new MementoCommand<TempoMap>(tmap, _before_state, &after));
+ _editor->commit_reversible_command ();
+
+ TempoSection* prev = 0;
+ if ((prev = tmap.previous_tempo_section (_tempo)) != 0) {
+ _editor->tempo_curve_selected (prev, false);
+ }
+
+ if (_tempo->clamped()) {
+ _editor->tempo_curve_selected (_tempo, false);
+
+ }
+}
+
+void
+TempoEndDrag::aborted (bool moved)
+{
+ if (moved) {
+ _editor->session()->tempo_map().set_state (*_before_state, Stateful::current_state_version);
+ }
+}
+
+CursorDrag::CursorDrag (Editor* e, EditorCursor& c, bool s)
+ : Drag (e, &c.track_canvas_item(), false)
+ , _cursor (c)
+ , _stop (s)
+ , _grab_zoom (0.0)
+{
+ DEBUG_TRACE (DEBUG::Drags, "New CursorDrag\n");
+}
+
+/** Do all the things we do when dragging the playhead to make it look as though
+ * we have located, without actually doing the locate (because that would cause
+ * the diskstream buffers to be refilled, which is too slow).
+ */
+void
+CursorDrag::fake_locate (framepos_t t)
+{
+ if (_editor->session () == 0) {
+ return;
+ }
+
+ _editor->playhead_cursor->set_position (t);
+
+ Session* s = _editor->session ();
+ if (s->timecode_transmission_suspended ()) {
+ framepos_t const f = _editor->playhead_cursor->current_frame ();
+ /* This is asynchronous so it will be sent "now"
+ */
+ s->send_mmc_locate (f);
+ /* These are synchronous and will be sent during the next
+ process cycle
+ */
+ s->queue_full_time_code ();
+ s->queue_song_position_pointer ();
+ }
+
+ show_verbose_cursor_time (t);
+ _editor->UpdateAllTransportClocks (t);
+}
+
+void
+CursorDrag::start_grab (GdkEvent* event, Gdk::Cursor* c)
+{
+ Drag::start_grab (event, c);
+ setup_snap_delta (MusicFrame (_editor->playhead_cursor->current_frame(), 0));
+
+ _grab_zoom = _editor->samples_per_pixel;
+
+ MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
+
+ _editor->snap_to_with_modifier (where, event);
+ _editor->_dragging_playhead = true;
+ _editor->_control_scroll_target = where.frame;
+
+ Session* s = _editor->session ();
+
+ /* grab the track canvas item as well */
+
+ _cursor.track_canvas_item().grab();
+
+ if (s) {
+ if (_was_rolling && _stop) {
+ s->request_stop ();
+ }
+
+ if (s->is_auditioning()) {
+ s->cancel_audition ();
+ }
+
+
+ if (AudioEngine::instance()->running()) {
+
+ /* do this only if we're the engine is connected
+ * because otherwise this request will never be
+ * serviced and we'll busy wait forever. likewise,
+ * notice if we are disconnected while waiting for the
+ * request to be serviced.
+ */
+
+ s->request_suspend_timecode_transmission ();
+ while (AudioEngine::instance()->running() && !s->timecode_transmission_suspended ()) {
+ /* twiddle our thumbs */
+ }
+ }
+ }
+
+ fake_locate (where.frame - snap_delta (event->button.state));
+}
+
+void
+CursorDrag::motion (GdkEvent* event, bool)
+{
+ MusicFrame where (_editor->canvas_event_sample (event) + snap_delta (event->button.state), 0);
+
+ _editor->snap_to_with_modifier (where, event);
+
+ if (where.frame != last_pointer_frame()) {
+ fake_locate (where.frame - snap_delta (event->button.state));
+ }
+}
+
+void
+CursorDrag::finished (GdkEvent* event, bool movement_occurred)
+{
+ _editor->_dragging_playhead = false;
+
+ _cursor.track_canvas_item().ungrab();
+
+ if (!movement_occurred && _stop) {
+ return;
+ }
+
+ motion (event, false);