+
+ // care about notes being moved beyond the upper/lower bounds on the canvas
+ if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
+ highest_note_in_selection > midi_stream_view()->highest_note()) {
+ midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
+ }
+ }
+}
+
+nframes64_t
+MidiRegionView::snap_to_frame(double x)
+{
+ PublicEditor &editor = trackview.editor();
+ // x is region relative
+ // convert x to global frame
+ nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
+ editor.snap_to(frame);
+ // convert event_frame back to local coordinates relative to position
+ frame -= _region->position();
+ return frame;
+}
+
+nframes64_t
+MidiRegionView::snap_to_frame(nframes64_t x)
+{
+ PublicEditor &editor = trackview.editor();
+ // x is region relative
+ // convert x to global frame
+ nframes64_t frame = x + _region->position();
+ editor.snap_to(frame);
+ // convert event_frame back to local coordinates relative to position
+ frame -= _region->position();
+ return frame;
+}
+
+double
+MidiRegionView::snap_to_pixel(double x)
+{
+ return (double) trackview.editor().frame_to_pixel(snap_to_frame(x));
+}
+
+double
+MidiRegionView::get_position_pixels()
+{
+ nframes64_t region_frame = get_position();
+ return trackview.editor().frame_to_pixel(region_frame);
+}
+
+void
+MidiRegionView::begin_resizing(CanvasNote::NoteEnd note_end)
+{
+ _resize_data.clear();
+
+ for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
+ CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
+
+ // only insert CanvasNotes into the map
+ if (note) {
+ NoteResizeData *resize_data = new NoteResizeData();
+ resize_data->canvas_note = note;
+
+ // create a new SimpleRect from the note which will be the resize preview
+ SimpleRect *resize_rect =
+ new SimpleRect(
+ *group,
+ note->x1(),
+ note->y1(),
+ note->x2(),
+ note->y2());
+
+ // calculate the colors: get the color settings
+ uint32_t fill_color =
+ UINT_RGBA_CHANGE_A(
+ ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
+ 128);
+
+ // make the resize preview notes more transparent and bright
+ fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
+
+ // calculate color based on note velocity
+ resize_rect->property_fill_color_rgba() =
+ UINT_INTERPOLATE(
+ CanvasNoteEvent::meter_style_fill_color(note->note()->velocity()),
+ fill_color,
+ 0.85);
+
+ resize_rect->property_outline_color_rgba() =
+ CanvasNoteEvent::calculate_outline(ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
+
+ resize_data->resize_rect = resize_rect;
+
+ if (note_end == CanvasNote::NOTE_ON) {
+ resize_data->current_x = note->x1();
+ } else { // NOTE_OFF
+ resize_data->current_x = note->x2();
+ }
+
+ _resize_data.push_back(resize_data);
+ }
+ }
+}
+
+void
+MidiRegionView::update_resizing(CanvasNote::NoteEnd note_end, double x, bool relative)
+{
+ for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
+ SimpleRect *resize_rect = (*i)->resize_rect;
+ CanvasNote *canvas_note = (*i)->canvas_note;
+
+ const double region_start = get_position_pixels();
+
+ if (relative) {
+ (*i)->current_x = (*i)->current_x + x;
+ } else {
+ // x is in track relative, transform it to region relative
+ (*i)->current_x = x - region_start;
+ }
+
+ double current_x = (*i)->current_x;
+
+ if (note_end == CanvasNote::NOTE_ON) {
+ resize_rect->property_x1() = snap_to_pixel(current_x);
+ resize_rect->property_x2() = canvas_note->x2();
+ } else {
+ resize_rect->property_x2() = snap_to_pixel(current_x);
+ resize_rect->property_x1() = canvas_note->x1();
+ }
+ }
+}
+
+void
+MidiRegionView::commit_resizing(CanvasNote::NoteEnd note_end, double event_x, bool relative)
+{
+ start_delta_command(_("resize notes"));
+
+ for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
+ CanvasNote* canvas_note = (*i)->canvas_note;
+ SimpleRect* resize_rect = (*i)->resize_rect;
+ double current_x = (*i)->current_x;
+ const double position = get_position_pixels();
+
+ if (!relative) {
+ // event_x is in track relative, transform it to region relative
+ current_x = event_x - position;
+ }
+
+ // because snapping works on world coordinates we have to transform current_x
+ // to world coordinates before snapping and transform it back afterwards
+ nframes64_t current_frame = snap_to_frame(current_x);
+ // transform to region start relative
+ current_frame += _region->start();
+
+ const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(canvas_note->note().get())));
+
+ // resize beginning of note
+ if (note_end == CanvasNote::NOTE_ON && current_frame < copy->end_time()) {
+ command_remove_note(canvas_note);
+ copy->on_event().time() = current_frame;
+ command_add_note(copy, _selection.find(canvas_note) != _selection.end());
+ }
+ // resize end of note
+ if (note_end == CanvasNote::NOTE_OFF && current_frame > copy->time()) {
+ command_remove_note(canvas_note);
+ copy->off_event().time() = current_frame;
+ command_add_note(copy, _selection.find(canvas_note) != _selection.end());
+ }
+
+ delete resize_rect;
+ delete (*i);
+ }
+
+ _resize_data.clear();
+ apply_command();
+}
+
+void
+MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
+{
+ const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(event->note().get())));
+
+ if (relative) {
+ uint8_t new_velocity = copy->velocity() + velocity;
+ clamp_0_to_127(new_velocity);
+ copy->set_velocity(new_velocity);
+ } else {
+ copy->set_velocity(velocity);
+ }
+
+ command_remove_note(event);
+ command_add_note(copy, event->selected());
+}
+
+void
+MidiRegionView::change_velocity(CanvasNoteEvent* ev, int8_t velocity, bool relative)
+{
+ start_delta_command(_("change velocity"));
+
+ change_note_velocity(ev, velocity, relative);
+
+ for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
+ Selection::iterator next = i;
+ ++next;
+ if ( !(*((*i)->note()) == *(ev->note())) ) {
+ change_note_velocity(*i, velocity, relative);
+ }
+ i = next;