+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;
+ }
+
+ apply_command();
+}
+
+void
+MidiRegionView::change_channel(uint8_t channel)
+{
+ start_delta_command(_("change channel"));
+ for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
+ Selection::iterator next = i;
+ ++next;
+
+ CanvasNoteEvent* event = *i;
+ const boost::shared_ptr<Evoral::Note> copy(new Evoral::Note(*(event->note().get())));
+
+ copy->set_channel(channel);
+
+ command_remove_note(event);
+ command_add_note(copy, event->selected());
+
+ i = next;
+ }
+
+ apply_command();
+}
+
+
+void
+MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
+{
+ if (ev->note() && _mouse_state == EraseTouchDragging) {
+ start_delta_command(_("note entered"));
+ ev->selected(true);
+ _delta_command->remove(ev->note());
+ } else if (_mouse_state == SelectTouchDragging) {
+ note_selected(ev, true);
+ }
+}
+
+
+void
+MidiRegionView::switch_source(boost::shared_ptr<Source> src)
+{
+ boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
+ if (msrc)
+ display_model(msrc->model());
+}
+
+void
+MidiRegionView::set_frame_color()
+{
+ if (frame) {
+ if (_selected && should_show_selection) {
+ frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
+ } else {
+ frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
+ }
+ }
+}
+
+void
+MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
+{
+ switch (mode) {
+ case AllChannels:
+ case FilterChannels:
+ _force_channel = -1;
+ break;
+ case ForceChannel:
+ _force_channel = mask;
+ mask = 0xFFFF; // Show all notes as active (below)
+ };
+
+ // Update notes for selection
+ for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
+ (*i)->on_channel_selection_change(mask);
+ }
+
+ _last_channel_selection = mask;
+}
+
+void
+MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
+{
+ _model_name = model;
+ _custom_device_mode = custom_device_mode;
+ redisplay_model();
+}