+ _step_edit_cursor_width = beats;
+
+ if (_step_edit_cursor) {
+ _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
+ }
+}
+
+/** Called when a diskstream on our track has received some data. Update the view, if applicable.
+ * @param w Source that the data will end up in.
+ */
+void
+MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
+{
+ if (!_active_notes) {
+ /* we aren't actively being recorded to */
+ return;
+ }
+
+ boost::shared_ptr<MidiSource> src = w.lock ();
+ if (!src || src != midi_region()->midi_source()) {
+ /* recorded data was not destined for our source */
+ return;
+ }
+
+ MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
+
+ boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
+
+ BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
+
+ framepos_t back = max_framepos;
+
+ for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
+ Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
+ assert (ev.buffer ());
+
+ Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
+
+ if (ev.type() == MIDI_CMD_NOTE_ON) {
+
+ boost::shared_ptr<NoteType> note (
+ new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
+ );
+
+ add_note (note, true);
+
+ /* fix up our note range */
+ if (ev.note() < _current_range_min) {
+ midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
+ } else if (ev.note() > _current_range_max) {
+ midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
+ }
+
+ } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
+ resolve_note (ev.note (), time_beats);
+ }
+
+ back = ev.time ();
+ }
+
+ midi_stream_view()->check_record_layers (region(), back);
+}
+
+void
+MidiRegionView::trim_front_starting ()
+{
+ /* Reparent the note group to the region view's parent, so that it doesn't change
+ when the region view is trimmed.
+ */
+ _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
+ _temporary_note_group->move (group->property_x(), group->property_y());
+ _note_group->reparent (*_temporary_note_group);
+}
+
+void
+MidiRegionView::trim_front_ending ()
+{
+ _note_group->reparent (*group);
+ delete _temporary_note_group;
+ _temporary_note_group = 0;
+
+ if (_region->start() < 0) {
+ /* Trim drag made start time -ve; fix this */
+ midi_region()->fix_negative_start ();
+ }
+}
+
+void
+MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
+{
+ PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
+ if (d.run () != Gtk::RESPONSE_ACCEPT) {
+ return;
+ }
+
+ change_patch_change (pc->patch(), d.patch ());
+}
+
+
+void
+MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
+{
+ char buf[24];
+ snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
+ Evoral::midi_note_name (n->note()).c_str(),
+ (int) n->note (),
+ (int) n->channel() + 1,
+ (int) n->velocity());
+
+ show_verbose_cursor (buf, 10, 20);
+}
+
+void
+MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
+{
+ double wx, wy;
+
+ trackview.editor().get_pointer_position (wx, wy);
+
+ wx += xoffset;
+ wy += yoffset;
+
+ /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
+
+ double x1, y1, x2, y2;
+ trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
+
+ if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
+ wy -= (y2 - y1) + 2 * yoffset;
+ }
+
+ trackview.editor().verbose_cursor()->set (text, wx, wy);
+ trackview.editor().verbose_cursor()->show ();
+}
+
+/** @param p A session framepos.
+ * @param grid_frames Filled in with the number of frames that a grid interval is at p.
+ * @return p snapped to the grid subdivision underneath it.
+ */
+framepos_t
+MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
+{
+ PublicEditor& editor = trackview.editor ();
+
+ bool success;
+ Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
+
+ if (!success) {
+ grid_beats = 1;
+ }
+
+ grid_frames = region_beats_to_region_frames (grid_beats);
+
+ /* Hack so that we always snap to the note that we are over, instead of snapping
+ to the next one if we're more than halfway through the one we're over.
+ */
+ if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
+ p -= grid_frames / 2;
+ }