2 Copyright (C) 2001-2007 Paul Davis
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <gtkmm2ext/gtk_ui.h>
29 #include <sigc++/signal.h>
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
34 #include "ardour/playlist.h"
35 #include "ardour/tempo.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_model.h"
39 #include "ardour/midi_patch_manager.h"
40 #include "ardour/session.h"
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIParameters.hpp"
44 #include "evoral/Control.hpp"
45 #include "evoral/midi_util.h"
47 #include "automation_region_view.h"
48 #include "automation_time_axis.h"
49 #include "canvas-hit.h"
50 #include "canvas-note.h"
51 #include "canvas-program-change.h"
52 #include "ghostregion.h"
53 #include "gui_thread.h"
55 #include "midi_cut_buffer.h"
56 #include "midi_list_editor.h"
57 #include "midi_region_view.h"
58 #include "midi_streamview.h"
59 #include "midi_time_axis.h"
60 #include "midi_time_axis.h"
61 #include "midi_util.h"
62 #include "public_editor.h"
63 #include "rgb_macros.h"
64 #include "selection.h"
65 #include "simpleline.h"
66 #include "streamview.h"
71 using namespace ARDOUR;
73 using namespace Editing;
74 using namespace ArdourCanvas;
75 using Gtkmm2ext::Keyboard;
77 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
78 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
79 : RegionView (parent, tv, r, spu, basic_color)
81 , _last_channel_selection(0xFFFF)
82 , _current_range_min(0)
83 , _current_range_max(0)
84 , _model_name(string())
85 , _custom_device_mode(string())
87 , _note_group(new ArdourCanvas::Group(*parent))
91 , _step_edit_cursor (0)
92 , _step_edit_cursor_width (1.0)
93 , _step_edit_cursor_position (0.0)
97 , _optimization_iterator (_events.end())
99 , no_sound_notes (false)
101 _note_group->raise_to_top();
102 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
105 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
106 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
107 TimeAxisViewItem::Visibility visibility)
108 : RegionView (parent, tv, r, spu, basic_color, false, visibility)
110 , _last_channel_selection(0xFFFF)
111 , _model_name(string())
112 , _custom_device_mode(string())
114 , _note_group(new ArdourCanvas::Group(*parent))
120 , _sort_needed (true)
121 , _optimization_iterator (_events.end())
123 , no_sound_notes (false)
125 _note_group->raise_to_top();
126 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
129 MidiRegionView::MidiRegionView (const MidiRegionView& other)
130 : sigc::trackable(other)
133 , _last_channel_selection(0xFFFF)
134 , _model_name(string())
135 , _custom_device_mode(string())
137 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
143 , _sort_needed (true)
144 , _optimization_iterator (_events.end())
146 , no_sound_notes (false)
151 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
152 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
157 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
158 : RegionView (other, boost::shared_ptr<Region> (region))
160 , _last_channel_selection(0xFFFF)
161 , _model_name(string())
162 , _custom_device_mode(string())
164 , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
170 , _sort_needed (true)
171 , _optimization_iterator (_events.end())
173 , no_sound_notes (false)
178 UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
179 c.set_rgb_p (r/255.0, g/255.0, b/255.0);
185 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
187 PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
189 CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
190 ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
194 midi_region()->midi_source(0)->load_model();
197 _model = midi_region()->midi_source(0)->model();
198 _enable_display = false;
200 RegionView::init (basic_color, false);
202 compute_colors (basic_color);
204 set_height (trackview.current_height());
207 region_sync_changed ();
208 region_resized (ARDOUR::bounds_change);
211 reset_width_dependent_items (_pixel_width);
215 _enable_display = true;
218 display_model (_model);
222 group->raise_to_top();
223 group->signal_event().connect (sigc::mem_fun (this, &MidiRegionView::canvas_event), false);
225 midi_view()->signal_channel_mode_changed().connect(
226 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
228 midi_view()->signal_midi_patch_settings_changed().connect(
229 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
231 trackview.editor().SnapChanged.connect (snap_changed_connection, invalidator (*this), ui_bind (&MidiRegionView::snap_changed, this), gui_context ());
235 MidiRegionView::canvas_event(GdkEvent* ev)
237 if (!trackview.editor().internal_editing()) {
241 /* XXX: note that until version 2.30, the GnomeCanvas did not propagate scroll events
242 to its items, which means that ev->type == GDK_SCROLL will never be seen
247 return scroll (&ev->scroll);
250 return key_press (&ev->key);
252 case GDK_KEY_RELEASE:
253 return key_release (&ev->key);
255 case GDK_BUTTON_PRESS:
256 return button_press (&ev->button);
258 case GDK_2BUTTON_PRESS:
261 case GDK_BUTTON_RELEASE:
262 return button_release (&ev->button);
264 case GDK_ENTER_NOTIFY:
265 return enter_notify (&ev->crossing);
267 case GDK_LEAVE_NOTIFY:
268 return leave_notify (&ev->crossing);
270 case GDK_MOTION_NOTIFY:
271 return motion (&ev->motion);
281 MidiRegionView::enter_notify (GdkEventCrossing* ev)
283 /* FIXME: do this on switch to note tool, too, if the pointer is already in */
285 Keyboard::magic_widget_grab_focus();
288 if (trackview.editor().current_mouse_mode() == MouseRange) {
289 create_ghost_note (ev->x, ev->y);
296 MidiRegionView::leave_notify (GdkEventCrossing* ev)
298 trackview.editor().hide_verbose_canvas_cursor ();
305 MidiRegionView::button_press (GdkEventButton* ev)
309 group->w2i (_last_x, _last_y);
311 if (_mouse_state != SelectTouchDragging && ev->button == 1) {
312 _pressed_button = ev->button;
313 _mouse_state = Pressed;
317 _pressed_button = ev->button;
323 MidiRegionView::button_release (GdkEventButton* ev)
325 double event_x, event_y;
326 nframes64_t event_frame = 0;
330 group->w2i(event_x, event_y);
331 group->ungrab(ev->time);
332 event_frame = trackview.editor().pixel_to_frame(event_x);
334 if (ev->button == 3) {
336 } else if (_pressed_button != 1) {
340 switch (_mouse_state) {
341 case Pressed: // Clicked
342 switch (trackview.editor().current_mouse_mode()) {
346 maybe_select_by_position (ev, event_x, event_y);
352 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, trackview.editor().pixel_to_frame (event_x));
356 create_note_at (event_x, event_y, beats, true);
364 case SelectRectDragging: // Select drag done
370 case AddDragging: // Add drag done
372 if (_drag_rect->property_x2() > _drag_rect->property_x1() + 2) {
373 const double x = _drag_rect->property_x1();
374 const double length = trackview.editor().pixel_to_frame
375 (_drag_rect->property_x2() - _drag_rect->property_x1());
377 create_note_at (x, _drag_rect->property_y1(), frames_to_beats(length), false);
383 create_ghost_note (ev->x, ev->y);
393 MidiRegionView::motion (GdkEventMotion* ev)
395 double event_x, event_y;
396 nframes64_t event_frame = 0;
400 group->w2i(event_x, event_y);
402 // convert event_x to global frame
403 event_frame = trackview.editor().pixel_to_frame(event_x) + _region->position();
404 trackview.editor().snap_to(event_frame);
405 // convert event_frame back to local coordinates relative to position
406 event_frame -= _region->position();
409 update_ghost_note (ev->x, ev->y);
412 /* any motion immediately hides velocity text that may have been visible */
414 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
415 (*i)->hide_velocity ();
418 switch (_mouse_state) {
419 case Pressed: // Maybe start a drag, if we've moved a bit
421 if (fabs (event_x - _last_x) < 1 && fabs (event_y - _last_y) < 1) {
422 /* no appreciable movement since the button was pressed */
427 if (_pressed_button == 1 && trackview.editor().current_mouse_mode() == MouseObject) {
428 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
429 Gdk::Cursor(Gdk::FLEUR), ev->time);
432 _drag_start_x = event_x;
433 _drag_start_y = event_y;
435 _drag_rect = new ArdourCanvas::SimpleRect(*group);
436 _drag_rect->property_x1() = event_x;
437 _drag_rect->property_y1() = event_y;
438 _drag_rect->property_x2() = event_x;
439 _drag_rect->property_y2() = event_y;
440 _drag_rect->property_outline_what() = 0xFF;
441 _drag_rect->property_outline_color_rgba()
442 = ARDOUR_UI::config()->canvasvar_MidiSelectRectOutline.get();
443 _drag_rect->property_fill_color_rgba()
444 = ARDOUR_UI::config()->canvasvar_MidiSelectRectFill.get();
446 _mouse_state = SelectRectDragging;
449 // Add note drag start
450 } else if (trackview.editor().internal_editing()) {
455 group->grab(GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
456 Gdk::Cursor(Gdk::FLEUR), ev->time);
459 _drag_start_x = event_x;
460 _drag_start_y = event_y;
462 _drag_rect = new ArdourCanvas::SimpleRect(*group);
463 _drag_rect->property_x1() = trackview.editor().frame_to_pixel(event_frame);
465 _drag_rect->property_y1() = midi_stream_view()->note_to_y(
466 midi_stream_view()->y_to_note(event_y));
467 _drag_rect->property_x2() = trackview.editor().frame_to_pixel(event_frame);
468 _drag_rect->property_y2() = _drag_rect->property_y1()
469 + floor(midi_stream_view()->note_height());
470 _drag_rect->property_outline_what() = 0xFF;
471 _drag_rect->property_outline_color_rgba() = 0xFFFFFF99;
472 _drag_rect->property_fill_color_rgba() = 0xFFFFFF66;
474 _mouse_state = AddDragging;
480 case SelectRectDragging: // Select drag motion
481 case AddDragging: // Add note drag motion
485 GdkModifierType state;
486 gdk_window_get_pointer(ev->window, &t_x, &t_y, &state);
491 if (_mouse_state == AddDragging)
492 event_x = trackview.editor().frame_to_pixel(event_frame);
495 if (event_x > _drag_start_x)
496 _drag_rect->property_x2() = event_x;
498 _drag_rect->property_x1() = event_x;
501 if (_drag_rect && _mouse_state == SelectRectDragging) {
502 if (event_y > _drag_start_y)
503 _drag_rect->property_y2() = event_y;
505 _drag_rect->property_y1() = event_y;
507 update_drag_selection(_drag_start_x, event_x, _drag_start_y, event_y);
513 case SelectTouchDragging:
525 MidiRegionView::scroll (GdkEventScroll* ev)
527 if (_selection.empty()) {
531 trackview.editor().hide_verbose_canvas_cursor ();
533 bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
535 if (ev->direction == GDK_SCROLL_UP) {
536 change_velocities (true, fine, false);
537 } else if (ev->direction == GDK_SCROLL_DOWN) {
538 change_velocities (false, fine, false);
544 MidiRegionView::key_press (GdkEventKey* ev)
546 /* since GTK bindings are generally activated on press, and since
547 detectable auto-repeat is the name of the game and only sends
548 repeated presses, carry out key actions at key press, not release.
551 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R){
552 _mouse_state = SelectTouchDragging;
555 } else if (ev->keyval == GDK_Escape) {
559 } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
561 bool start = (ev->keyval == GDK_comma);
562 bool end = (ev->keyval == GDK_period);
563 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
564 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
566 change_note_lengths (fine, shorter, start, end);
570 } else if (ev->keyval == GDK_Delete) {
575 } else if (ev->keyval == GDK_Tab) {
577 if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
578 goto_previous_note ();
584 } else if (ev->keyval == GDK_Up) {
586 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
587 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
589 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
590 change_velocities (true, fine, allow_smush);
592 transpose (true, fine, allow_smush);
596 } else if (ev->keyval == GDK_Down) {
598 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
599 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
601 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
602 change_velocities (false, fine, allow_smush);
604 transpose (false, fine, allow_smush);
608 } else if (ev->keyval == GDK_Left) {
613 } else if (ev->keyval == GDK_Right) {
618 } else if (ev->keyval == GDK_Control_L) {
627 MidiRegionView::key_release (GdkEventKey* ev)
629 if (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R) {
637 MidiRegionView::show_list_editor ()
640 _list_editor = new MidiListEditor (trackview.session(), midi_region());
642 _list_editor->present ();
645 /** Add a note to the model, and the view, at a canvas (click) coordinate.
646 * \param x horizontal position in pixels
647 * \param y vertical position in pixels
648 * \param length duration of the note in beats, which will be snapped to the grid
649 * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
652 MidiRegionView::create_note_at(double x, double y, double length, bool sh)
654 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
655 MidiStreamView* const view = mtv->midi_view();
657 double note = midi_stream_view()->y_to_note(y);
660 assert(note <= 127.0);
662 // Start of note in frames relative to region start
663 nframes64_t const start_frames = snap_frame_to_frame(trackview.editor().pixel_to_frame(x));
664 assert(start_frames >= 0);
667 length = frames_to_beats(
668 snap_frame_to_frame(start_frames + beats_to_frames(length)) - start_frames);
670 assert (length != 0);
673 length = frames_to_beats (beats_to_frames (length) - 1);
676 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
680 /* pick the highest selected channel, unless all channels are selected,
681 which is interpreted to mean channel 1 (zero)
684 for (uint16_t i = 0; i < 16; ++i) {
685 if (chn_mask & (1<<i)) {
695 const boost::shared_ptr<NoteType> new_note (new NoteType (channel,
696 frames_to_beats(start_frames + _region->start()), length,
697 (uint8_t)note, 0x40));
699 if (_model->contains (new_note)) {
703 view->update_note_range(new_note->note());
705 MidiModel::DiffCommand* cmd = _model->new_diff_command("add note");
707 _model->apply_command(*trackview.session(), cmd);
709 play_midi_note (new_note);
713 MidiRegionView::clear_events()
718 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
719 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
724 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
729 _pgm_changes.clear();
731 _optimization_iterator = _events.end();
736 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
739 content_connection.disconnect ();
740 _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
744 if (_enable_display) {
750 MidiRegionView::start_diff_command(string name)
752 if (!_diff_command) {
753 _diff_command = _model->new_diff_command(name);
758 MidiRegionView::diff_add_note(const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
761 _diff_command->add(note);
764 _marked_for_selection.insert(note);
767 _marked_for_velocity.insert(note);
772 MidiRegionView::diff_remove_note(ArdourCanvas::CanvasNoteEvent* ev)
774 if (_diff_command && ev->note()) {
775 _diff_command->remove(ev->note());
780 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
781 MidiModel::DiffCommand::Property property,
785 _diff_command->change (ev->note(), property, val);
790 MidiRegionView::diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
791 MidiModel::DiffCommand::Property property,
792 Evoral::MusicalTime val)
795 _diff_command->change (ev->note(), property, val);
800 MidiRegionView::apply_diff ()
804 if (!_diff_command) {
808 if ((add_or_remove = _diff_command->adds_or_removes())) {
809 // Mark all selected notes for selection when model reloads
810 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
811 _marked_for_selection.insert((*i)->note());
815 _model->apply_command(*trackview.session(), _diff_command);
817 midi_view()->midi_track()->playlist_modified();
820 _marked_for_selection.clear();
823 _marked_for_velocity.clear();
827 MidiRegionView::apply_diff_as_subcommand()
831 if (!_diff_command) {
835 if ((add_or_remove = _diff_command->adds_or_removes())) {
836 // Mark all selected notes for selection when model reloads
837 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
838 _marked_for_selection.insert((*i)->note());
842 _model->apply_command_as_subcommand(*trackview.session(), _diff_command);
844 midi_view()->midi_track()->playlist_modified();
847 _marked_for_selection.clear();
849 _marked_for_velocity.clear();
854 MidiRegionView::abort_command()
856 delete _diff_command;
862 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
864 if (_optimization_iterator != _events.end()) {
865 ++_optimization_iterator;
868 if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
869 return *_optimization_iterator;
872 for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
873 if ((*_optimization_iterator)->note() == note) {
874 return *_optimization_iterator;
882 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
884 MidiModel::Notes notes;
885 _model->get_notes (notes, op, val, chan_mask);
887 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
888 CanvasNoteEvent* cne = find_canvas_note (*n);
896 MidiRegionView::redisplay_model()
898 // Don't redisplay the model if we're currently recording and displaying that
904 cerr << "MidiRegionView::redisplay_model called without a model" << endmsg;
908 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
912 MidiModel::ReadLock lock(_model->read_lock());
914 MidiModel::Notes& notes (_model->notes());
915 _optimization_iterator = _events.begin();
917 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
919 boost::shared_ptr<NoteType> note (*n);
920 CanvasNoteEvent* cne;
923 if (note_in_region_range (note, visible)) {
925 if ((cne = find_canvas_note (note)) != 0) {
932 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
934 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
946 add_note (note, visible);
951 if ((cne = find_canvas_note (note)) != 0) {
959 /* remove note items that are no longer valid */
961 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
962 if (!(*i)->valid ()) {
964 i = _events.erase (i);
970 _pgm_changes.clear();
974 display_program_changes();
976 _marked_for_selection.clear ();
977 _marked_for_velocity.clear ();
979 /* we may have caused _events to contain things out of order (e.g. if a note
980 moved earlier or later). we don't generally need them in time order, but
981 make a note that a sort is required for those cases that require it.
988 MidiRegionView::display_program_changes()
990 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
991 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
993 for (uint8_t i = 0; i < 16; ++i) {
994 if (chn_mask & (1<<i)) {
995 display_program_changes_on_channel (i);
1001 MidiRegionView::display_program_changes_on_channel(uint8_t channel)
1003 boost::shared_ptr<Evoral::Control> control =
1004 _model->control(Evoral::MIDI::ProgramChange (MidiPgmChangeAutomation, channel));
1010 Glib::Mutex::Lock lock (control->list()->lock());
1012 for (AutomationList::const_iterator event = control->list()->begin();
1013 event != control->list()->end(); ++event) {
1014 double event_time = (*event)->when;
1015 double program_number = floor((*event)->value + 0.5);
1017 // Get current value of bank select MSB at time of the program change
1018 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1019 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1021 if (msb_control != 0) {
1022 msb = uint8_t(floor(msb_control->get_double(true, event_time) + 0.5));
1025 // Get current value of bank select LSB at time of the program change
1026 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1027 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1029 if (lsb_control != 0) {
1030 lsb = uint8_t(floor(lsb_control->get_double(true, event_time) + 0.5));
1033 MIDI::Name::PatchPrimaryKey patch_key(msb, lsb, program_number);
1035 boost::shared_ptr<MIDI::Name::Patch> patch =
1036 MIDI::Name::MidiPatchManager::instance().find_patch(
1037 _model_name, _custom_device_mode, channel, patch_key);
1039 PCEvent program_change(event_time, uint8_t(program_number), channel);
1042 add_pgm_change(program_change, patch->name());
1045 // program_number is zero-based: convert to one-based
1046 snprintf(buf, 4, "%d", int(program_number+1));
1047 add_pgm_change(program_change, buf);
1053 MidiRegionView::display_sysexes()
1055 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1056 Evoral::MusicalTime time = (*i)->time();
1061 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1062 str << int((*i)->buffer()[b]);
1063 if (b != (*i)->size() -1) {
1067 string text = str.str();
1069 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1071 const double x = trackview.editor().frame_to_pixel(beats_to_frames(time));
1073 double height = midi_stream_view()->contents_height();
1075 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1076 new CanvasSysEx(*this, *group, text, height, x, 1.0));
1078 // Show unless program change is beyond the region bounds
1079 if (time - _region->start() >= _region->length() || time < _region->start()) {
1085 _sys_exes.push_back(sysex);
1090 MidiRegionView::~MidiRegionView ()
1092 in_destructor = true;
1094 trackview.editor().hide_verbose_canvas_cursor ();
1096 note_delete_connection.disconnect ();
1098 delete _list_editor;
1100 RegionViewGoingAway (this); /* EMIT_SIGNAL */
1102 if (_active_notes) {
1110 delete _diff_command;
1111 delete _step_edit_cursor;
1115 MidiRegionView::region_resized (const PropertyChange& what_changed)
1117 RegionView::region_resized(what_changed);
1119 if (what_changed.contains (ARDOUR::Properties::position)) {
1120 set_duration(_region->length(), 0);
1121 if (_enable_display) {
1128 MidiRegionView::reset_width_dependent_items (double pixel_width)
1130 RegionView::reset_width_dependent_items(pixel_width);
1131 assert(_pixel_width == pixel_width);
1133 if (_enable_display) {
1137 move_step_edit_cursor (_step_edit_cursor_position);
1138 set_step_edit_cursor_width (_step_edit_cursor_width);
1142 MidiRegionView::set_height (double height)
1144 static const double FUDGE = 2.0;
1145 const double old_height = _height;
1146 RegionView::set_height(height);
1147 _height = height - FUDGE;
1149 apply_note_range(midi_stream_view()->lowest_note(),
1150 midi_stream_view()->highest_note(),
1151 height != old_height + FUDGE);
1154 name_pixbuf->raise_to_top();
1157 for (PgmChanges::iterator x = _pgm_changes.begin(); x != _pgm_changes.end(); ++x) {
1158 (*x)->set_height (midi_stream_view()->contents_height());
1161 if (_step_edit_cursor) {
1162 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1167 /** Apply the current note range from the stream view
1168 * by repositioning/hiding notes as necessary
1171 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1173 if (!_enable_display) {
1177 if (!force && _current_range_min == min && _current_range_max == max) {
1181 _current_range_min = min;
1182 _current_range_max = max;
1184 for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1185 CanvasNoteEvent* event = *i;
1186 boost::shared_ptr<NoteType> note (event->note());
1188 if (note->note() < _current_range_min ||
1189 note->note() > _current_range_max) {
1195 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1197 const double y1 = midi_stream_view()->note_to_y(note->note());
1198 const double y2 = y1 + floor(midi_stream_view()->note_height());
1200 cnote->property_y1() = y1;
1201 cnote->property_y2() = y2;
1203 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1205 double x = trackview.editor().frame_to_pixel(
1206 beats_to_frames(note->time()) - _region->start());
1207 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1208 double y = midi_stream_view()->note_to_y(event->note()->note())
1209 + ((diamond_size-2.0) / 4.0);
1211 chit->set_height (diamond_size);
1212 chit->move (x - chit->x1(), y - chit->y1());
1219 MidiRegionView::add_ghost (TimeAxisView& tv)
1223 double unit_position = _region->position () / samples_per_unit;
1224 MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1225 MidiGhostRegion* ghost;
1227 if (mtv && mtv->midi_view()) {
1228 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1229 to allow having midi notes on top of note lines and waveforms.
1231 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1233 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1236 ghost->set_height ();
1237 ghost->set_duration (_region->length() / samples_per_unit);
1238 ghosts.push_back (ghost);
1240 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1241 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1242 ghost->add_note(note);
1246 GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1252 /** Begin tracking note state for successive calls to add_event
1255 MidiRegionView::begin_write()
1257 assert(!_active_notes);
1258 _active_notes = new CanvasNote*[128];
1259 for (unsigned i=0; i < 128; ++i) {
1260 _active_notes[i] = 0;
1265 /** Destroy note state for add_event
1268 MidiRegionView::end_write()
1270 delete[] _active_notes;
1272 _marked_for_selection.clear();
1273 _marked_for_velocity.clear();
1277 /** Resolve an active MIDI note (while recording).
1280 MidiRegionView::resolve_note(uint8_t note, double end_time)
1282 if (midi_view()->note_mode() != Sustained) {
1286 if (_active_notes && _active_notes[note]) {
1287 const nframes64_t end_time_frames = beats_to_frames(end_time);
1288 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1289 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1290 _active_notes[note] = 0;
1295 /** Extend active notes to rightmost edge of region (if length is changed)
1298 MidiRegionView::extend_active_notes()
1300 if (!_active_notes) {
1304 for (unsigned i=0; i < 128; ++i) {
1305 if (_active_notes[i]) {
1306 _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1312 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1314 if (no_sound_notes || !trackview.editor().sound_notes()) {
1318 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1321 route_ui->midi_track()->write_immediate_event(
1322 note->on_event().size(), note->on_event().buffer());
1324 const double note_length_beats = (note->off_event().time() - note->on_event().time());
1325 nframes_t note_length_ms = beats_to_frames(note_length_beats)
1326 * (1000 / (double)route_ui->session()->nominal_frame_rate());
1327 Glib::signal_timeout().connect(sigc::bind(sigc::mem_fun(this, &MidiRegionView::play_midi_note_off), note),
1328 note_length_ms, G_PRIORITY_DEFAULT);
1332 MidiRegionView::play_midi_note_off(boost::shared_ptr<NoteType> note)
1334 RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1337 route_ui->midi_track()->write_immediate_event(
1338 note->off_event().size(), note->off_event().buffer());
1344 MidiRegionView::note_in_region_range(const boost::shared_ptr<NoteType> note, bool& visible) const
1346 const nframes64_t note_start_frames = beats_to_frames(note->time());
1348 bool outside = (note_start_frames - _region->start() >= _region->length()) ||
1349 (note_start_frames < _region->start());
1351 visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1352 (note->note() <= midi_stream_view()->highest_note());
1358 MidiRegionView::update_note (CanvasNote* ev)
1360 boost::shared_ptr<NoteType> note = ev->note();
1362 const nframes64_t note_start_frames = beats_to_frames(note->time());
1364 /* trim note display to not overlap the end of its region */
1365 const nframes64_t note_end_frames = min (beats_to_frames (note->end_time()), _region->start() + _region->length());
1367 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1368 const double y1 = midi_stream_view()->note_to_y(note->note());
1369 const double note_endpixel = trackview.editor().frame_to_pixel(note_end_frames - _region->start());
1371 ev->property_x1() = x;
1372 ev->property_y1() = y1;
1373 if (note->length() > 0) {
1374 ev->property_x2() = note_endpixel;
1376 ev->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1378 ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1380 if (note->length() == 0) {
1381 if (_active_notes) {
1382 assert(note->note() < 128);
1383 // If this note is already active there's a stuck note,
1384 // finish the old note rectangle
1385 if (_active_notes[note->note()]) {
1386 CanvasNote* const old_rect = _active_notes[note->note()];
1387 boost::shared_ptr<NoteType> old_note = old_rect->note();
1388 old_rect->property_x2() = x;
1389 old_rect->property_outline_what() = (guint32) 0xF;
1391 _active_notes[note->note()] = ev;
1393 /* outline all but right edge */
1394 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1396 /* outline all edges */
1397 ev->property_outline_what() = (guint32) 0xF;
1402 MidiRegionView::update_hit (CanvasHit* ev)
1404 boost::shared_ptr<NoteType> note = ev->note();
1406 const nframes64_t note_start_frames = beats_to_frames(note->time());
1407 const double x = trackview.editor().frame_to_pixel(note_start_frames - _region->start());
1408 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1409 const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1414 /** Add a MIDI note to the view (with length).
1416 * If in sustained mode, notes with length 0 will be considered active
1417 * notes, and resolve_note should be called when the corresponding note off
1418 * event arrives, to properly display the note.
1421 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1423 CanvasNoteEvent* event = 0;
1425 assert(note->time() >= 0);
1426 assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1428 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1430 if (midi_view()->note_mode() == Sustained) {
1432 CanvasNote* ev_rect = new CanvasNote(*this, *group, note);
1434 update_note (ev_rect);
1438 MidiGhostRegion* gr;
1440 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1441 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1442 gr->add_note(ev_rect);
1446 } else if (midi_view()->note_mode() == Percussive) {
1448 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1450 CanvasHit* ev_diamond = new CanvasHit(*this, *group, diamond_size, note);
1452 update_hit (ev_diamond);
1461 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1462 note_selected(event, true);
1465 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1466 event->show_velocity();
1468 event->on_channel_selection_change(_last_channel_selection);
1469 _events.push_back(event);
1480 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1481 Evoral::MusicalTime pos, Evoral::MusicalTime len)
1483 boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1485 /* potentially extend region to hold new note */
1487 nframes64_t end_frame = _region->position() + beats_to_frames (new_note->end_time());
1488 nframes64_t region_end = _region->position() + _region->length() - 1;
1490 if (end_frame > region_end) {
1491 _region->set_length (end_frame - _region->position(), this);
1494 start_diff_command (_("step add"));
1495 diff_add_note (new_note, true, false);
1498 // last_step_edit_note = new_note;
1502 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1504 assert(program.time >= 0);
1506 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1507 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1509 double height = midi_stream_view()->contents_height();
1511 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1512 new CanvasProgramChange(*this, *group,
1517 _custom_device_mode,
1518 program.time, program.channel, program.value));
1520 // Show unless program change is beyond the region bounds
1521 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1527 _pgm_changes.push_back(pgm_change);
1531 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1533 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1534 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1536 if (msb_control != 0) {
1537 msb = int(msb_control->get_double(true, time));
1540 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1541 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1543 if (lsb_control != 0) {
1544 lsb = lsb_control->get_double(true, time);
1547 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1548 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1549 double program_number = -1.0;
1550 if (program_control != 0) {
1551 program_number = program_control->get_double(true, time);
1554 key.msb = (int) floor(msb + 0.5);
1555 key.lsb = (int) floor(lsb + 0.5);
1556 key.program_number = (int) floor(program_number + 0.5);
1557 assert(key.is_sane());
1562 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1564 // TODO: Get the real event here and alter them at the original times
1565 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1566 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1567 if (msb_control != 0) {
1568 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1571 // TODO: Get the real event here and alter them at the original times
1572 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1573 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1574 if (lsb_control != 0) {
1575 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1578 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1579 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1581 assert(program_control != 0);
1582 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1584 _pgm_changes.clear ();
1585 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1589 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1591 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1592 alter_program_change(program_change_event, new_patch);
1596 MidiRegionView::previous_program(CanvasProgramChange& program)
1598 if (program.program() < 127) {
1599 MIDI::Name::PatchPrimaryKey key;
1600 get_patch_key_at(program.event_time(), program.channel(), key);
1601 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1603 key.program_number++;
1604 alter_program_change(program_change_event, key);
1609 MidiRegionView::next_program(CanvasProgramChange& program)
1611 if (program.program() > 0) {
1612 MIDI::Name::PatchPrimaryKey key;
1613 get_patch_key_at(program.event_time(), program.channel(), key);
1614 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1616 key.program_number--;
1617 alter_program_change(program_change_event, key);
1622 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1624 if (_selection.empty()) {
1628 if (_selection.erase (cne) > 0) {
1629 cerr << "Erased a CNE from selection\n";
1634 MidiRegionView::delete_selection()
1636 if (_selection.empty()) {
1640 start_diff_command (_("delete selection"));
1642 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1643 if ((*i)->selected()) {
1644 _diff_command->remove((*i)->note());
1654 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1656 start_diff_command (_("delete note"));
1657 _diff_command->remove (n);
1660 trackview.editor().hide_verbose_canvas_cursor ();
1664 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1666 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1667 if ((*i)->selected() && (*i) != ev) {
1668 (*i)->set_selected(false);
1669 (*i)->hide_velocity();
1677 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1679 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1682 Selection::iterator tmp = i;
1685 (*i)->set_selected (false);
1686 _selection.erase (i);
1695 /* don't bother with removing this regionview from the editor selection,
1696 since we're about to add another note, and thus put/keep this
1697 regionview in the editor selection.
1700 if (!ev->selected()) {
1701 add_to_selection (ev);
1706 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1708 uint8_t low_note = 127;
1709 uint8_t high_note = 0;
1710 MidiModel::Notes& notes (_model->notes());
1711 _optimization_iterator = _events.begin();
1717 if (extend && _selection.empty()) {
1723 /* scan existing selection to get note range */
1725 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1726 if ((*i)->note()->note() < low_note) {
1727 low_note = (*i)->note()->note();
1729 if ((*i)->note()->note() > high_note) {
1730 high_note = (*i)->note()->note();
1734 low_note = min (low_note, notenum);
1735 high_note = max (high_note, notenum);
1738 no_sound_notes = true;
1740 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1742 boost::shared_ptr<NoteType> note (*n);
1743 CanvasNoteEvent* cne;
1744 bool select = false;
1746 if (((1 << note->channel()) & channel_mask) != 0) {
1748 if ((note->note() >= low_note && note->note() <= high_note)) {
1751 } else if (note->note() == notenum) {
1757 if ((cne = find_canvas_note (note)) != 0) {
1758 // extend is false because we've taken care of it,
1759 // since it extends by time range, not pitch.
1760 note_selected (cne, add, false);
1764 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1768 no_sound_notes = false;
1772 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1774 MidiModel::Notes& notes (_model->notes());
1775 _optimization_iterator = _events.begin();
1777 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1779 boost::shared_ptr<NoteType> note (*n);
1780 CanvasNoteEvent* cne;
1782 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1783 if ((cne = find_canvas_note (note)) != 0) {
1784 if (cne->selected()) {
1785 note_deselected (cne);
1787 note_selected (cne, true, false);
1795 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1798 clear_selection_except(ev);
1803 if (!ev->selected()) {
1804 add_to_selection (ev);
1808 /* find end of latest note selected, select all between that and the start of "ev" */
1810 Evoral::MusicalTime earliest = DBL_MAX;
1811 Evoral::MusicalTime latest = 0;
1813 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1814 if ((*i)->note()->end_time() > latest) {
1815 latest = (*i)->note()->end_time();
1817 if ((*i)->note()->time() < earliest) {
1818 earliest = (*i)->note()->time();
1822 if (ev->note()->end_time() > latest) {
1823 latest = ev->note()->end_time();
1826 if (ev->note()->time() < earliest) {
1827 earliest = ev->note()->time();
1830 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1832 /* find notes entirely within OR spanning the earliest..latest range */
1834 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1835 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1836 add_to_selection (*i);
1840 /* if events were guaranteed to be time sorted, we could do this.
1841 but as of sept 10th 2009, they no longer are.
1844 if ((*i)->note()->time() > latest) {
1853 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1855 remove_from_selection (ev);
1859 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1869 // TODO: Make this faster by storing the last updated selection rect, and only
1870 // adjusting things that are in the area that appears/disappeared.
1871 // We probably need a tree to be able to find events in O(log(n)) time.
1873 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1875 /* check if any corner of the note is inside the rect
1878 1) this is computing "touched by", not "contained by" the rect.
1879 2) this does not require that events be sorted in time.
1882 const double ix1 = (*i)->x1();
1883 const double ix2 = (*i)->x2();
1884 const double iy1 = (*i)->y1();
1885 const double iy2 = (*i)->y2();
1887 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1888 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1889 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1890 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1893 if (!(*i)->selected()) {
1894 add_to_selection (*i);
1896 } else if ((*i)->selected()) {
1897 // Not inside rectangle
1898 remove_from_selection (*i);
1904 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1906 Selection::iterator i = _selection.find (ev);
1908 if (i != _selection.end()) {
1909 _selection.erase (i);
1912 ev->set_selected (false);
1913 ev->hide_velocity ();
1915 if (_selection.empty()) {
1916 PublicEditor& editor (trackview.editor());
1917 editor.get_selection().remove (this);
1922 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1924 bool add_mrv_selection = false;
1926 if (_selection.empty()) {
1927 add_mrv_selection = true;
1930 if (_selection.insert (ev).second) {
1931 ev->set_selected (true);
1932 play_midi_note ((ev)->note());
1935 if (add_mrv_selection) {
1936 PublicEditor& editor (trackview.editor());
1937 editor.get_selection().add (this);
1942 MidiRegionView::move_selection(double dx, double dy)
1944 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1945 (*i)->move_event(dx, dy);
1950 MidiRegionView::note_dropped(CanvasNoteEvent *, double dt, int8_t dnote)
1952 assert (!_selection.empty());
1954 uint8_t lowest_note_in_selection = 127;
1955 uint8_t highest_note_in_selection = 0;
1956 uint8_t highest_note_difference = 0;
1958 // find highest and lowest notes first
1960 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1961 uint8_t pitch = (*i)->note()->note();
1962 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
1963 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
1967 cerr << "dnote: " << (int) dnote << endl;
1968 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1969 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1970 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1971 << int(highest_note_in_selection) << endl;
1972 cerr << "selection size: " << _selection.size() << endl;
1973 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1976 // Make sure the note pitch does not exceed the MIDI standard range
1977 if (highest_note_in_selection + dnote > 127) {
1978 highest_note_difference = highest_note_in_selection - 127;
1981 start_diff_command(_("move notes"));
1983 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
1985 nframes64_t start_frames = beats_to_frames((*i)->note()->time());
1988 start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
1990 start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
1993 Evoral::MusicalTime new_time = frames_to_beats(start_frames);
1999 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2001 uint8_t original_pitch = (*i)->note()->note();
2002 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2004 // keep notes in standard midi range
2005 clamp_to_0_127(new_pitch);
2007 // keep original pitch if note is dragged outside valid midi range
2008 if ((original_pitch != 0 && new_pitch == 0)
2009 || (original_pitch != 127 && new_pitch == 127)) {
2010 new_pitch = original_pitch;
2013 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2014 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2016 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2021 // care about notes being moved beyond the upper/lower bounds on the canvas
2022 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2023 highest_note_in_selection > midi_stream_view()->highest_note()) {
2024 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2029 MidiRegionView::snap_pixel_to_frame(double x)
2031 PublicEditor& editor = trackview.editor();
2032 // x is region relative, convert it to global absolute frames
2033 nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
2034 editor.snap_to(frame);
2035 return frame - _region->position(); // convert back to region relative
2039 MidiRegionView::snap_frame_to_frame(nframes64_t x)
2041 PublicEditor& editor = trackview.editor();
2042 // x is region relative, convert it to global absolute frames
2043 nframes64_t frame = x + _region->position();
2044 editor.snap_to(frame);
2045 return frame - _region->position(); // convert back to region relative
2049 MidiRegionView::snap_to_pixel(double x)
2051 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2055 MidiRegionView::get_position_pixels()
2057 nframes64_t region_frame = get_position();
2058 return trackview.editor().frame_to_pixel(region_frame);
2062 MidiRegionView::get_end_position_pixels()
2064 nframes64_t frame = get_position() + get_duration ();
2065 return trackview.editor().frame_to_pixel(frame);
2069 MidiRegionView::beats_to_frames(double beats) const
2071 return _time_converter.to(beats);
2075 MidiRegionView::frames_to_beats(nframes64_t frames) const
2077 return _time_converter.from(frames);
2081 MidiRegionView::begin_resizing (bool /*at_front*/)
2083 _resize_data.clear();
2085 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2086 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2088 // only insert CanvasNotes into the map
2090 NoteResizeData *resize_data = new NoteResizeData();
2091 resize_data->canvas_note = note;
2093 // create a new SimpleRect from the note which will be the resize preview
2094 SimpleRect *resize_rect = new SimpleRect(
2095 *group, note->x1(), note->y1(), note->x2(), note->y2());
2097 // calculate the colors: get the color settings
2098 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2099 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2102 // make the resize preview notes more transparent and bright
2103 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2105 // calculate color based on note velocity
2106 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2107 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2111 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2112 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2114 resize_data->resize_rect = resize_rect;
2115 _resize_data.push_back(resize_data);
2120 /** Update resizing notes while user drags.
2121 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2122 * @param at_front which end of the note (true == note on, false == note off)
2123 * @param delta_x change in mouse position since the start of the drag
2124 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2125 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2126 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2127 * as the \a primary note.
2130 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2132 bool cursor_set = false;
2134 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2135 SimpleRect* resize_rect = (*i)->resize_rect;
2136 CanvasNote* canvas_note = (*i)->canvas_note;
2141 current_x = canvas_note->x1() + delta_x;
2143 current_x = primary->x1() + delta_x;
2147 current_x = canvas_note->x2() + delta_x;
2149 current_x = primary->x2() + delta_x;
2154 resize_rect->property_x1() = snap_to_pixel(current_x);
2155 resize_rect->property_x2() = canvas_note->x2();
2157 resize_rect->property_x2() = snap_to_pixel(current_x);
2158 resize_rect->property_x1() = canvas_note->x1();
2164 beats = snap_pixel_to_frame (current_x);
2165 beats = frames_to_beats (beats);
2170 if (beats < canvas_note->note()->end_time()) {
2171 len = canvas_note->note()->time() - beats;
2172 len += canvas_note->note()->length();
2177 if (beats >= canvas_note->note()->end_time()) {
2178 len = beats - canvas_note->note()->time();
2185 snprintf (buf, sizeof (buf), "%.3g beats", len);
2186 trackview.editor().show_verbose_canvas_cursor_with (buf);
2195 /** Finish resizing notes when the user releases the mouse button.
2196 * Parameters the same as for \a update_resizing().
2199 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2201 start_diff_command(_("resize notes"));
2203 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2204 CanvasNote* canvas_note = (*i)->canvas_note;
2205 SimpleRect* resize_rect = (*i)->resize_rect;
2210 current_x = canvas_note->x1() + delta_x;
2212 current_x = primary->x1() + delta_x;
2216 current_x = canvas_note->x2() + delta_x;
2218 current_x = primary->x2() + delta_x;
2222 current_x = snap_pixel_to_frame (current_x);
2223 current_x = frames_to_beats (current_x);
2225 if (at_front && current_x < canvas_note->note()->end_time()) {
2226 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2228 double len = canvas_note->note()->time() - current_x;
2229 len += canvas_note->note()->length();
2232 /* XXX convert to beats */
2233 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2238 double len = current_x - canvas_note->note()->time();
2241 /* XXX convert to beats */
2242 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2250 _resize_data.clear();
2255 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2257 uint8_t new_velocity;
2260 new_velocity = event->note()->velocity() + velocity;
2261 clamp_to_0_127(new_velocity);
2263 new_velocity = velocity;
2266 event->set_selected (event->selected()); // change color
2268 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2272 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2277 new_note = event->note()->note() + note;
2282 clamp_to_0_127 (new_note);
2283 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2287 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2289 bool change_start = false;
2290 bool change_length = false;
2291 Evoral::MusicalTime new_start = 0;
2292 Evoral::MusicalTime new_length = 0;
2294 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2296 front_delta: if positive - move the start of the note later in time (shortening it)
2297 if negative - move the start of the note earlier in time (lengthening it)
2299 end_delta: if positive - move the end of the note later in time (lengthening it)
2300 if negative - move the end of the note earlier in time (shortening it)
2304 if (front_delta < 0) {
2306 if (event->note()->time() < -front_delta) {
2309 new_start = event->note()->time() + front_delta; // moves earlier
2312 /* start moved toward zero, so move the end point out to where it used to be.
2313 Note that front_delta is negative, so this increases the length.
2316 new_length = event->note()->length() - front_delta;
2317 change_start = true;
2318 change_length = true;
2322 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2324 if (new_pos < event->note()->end_time()) {
2325 new_start = event->note()->time() + front_delta;
2326 /* start moved toward the end, so move the end point back to where it used to be */
2327 new_length = event->note()->length() - front_delta;
2328 change_start = true;
2329 change_length = true;
2336 bool can_change = true;
2337 if (end_delta < 0) {
2338 if (event->note()->length() < -end_delta) {
2344 new_length = event->note()->length() + end_delta;
2345 change_length = true;
2350 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2353 if (change_length) {
2354 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2359 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2361 Evoral::MusicalTime new_time;
2365 if (event->note()->time() < -delta) {
2368 new_time = event->note()->time() + delta;
2371 new_time = event->note()->time() + delta;
2377 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2381 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2385 if (_selection.empty()) {
2400 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2401 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2407 start_diff_command(_("change velocities"));
2409 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2410 Selection::iterator next = i;
2412 change_note_velocity (*i, delta, true);
2418 if (!_selection.empty()) {
2420 snprintf (buf, sizeof (buf), "Vel %d",
2421 (int) (*_selection.begin())->note()->velocity());
2422 trackview.editor().show_verbose_canvas_cursor_with (buf);
2428 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2430 if (_selection.empty()) {
2447 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2449 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2453 if ((int8_t) (*i)->note()->note() + delta > 127) {
2460 start_diff_command (_("transpose"));
2462 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2463 Selection::iterator next = i;
2465 change_note_note (*i, delta, true);
2473 MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool end)
2475 Evoral::MusicalTime delta;
2480 /* grab the current grid distance */
2482 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2484 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2485 cerr << "Grid type not available as beats - TO BE FIXED\n";
2494 start_diff_command (_("change note lengths"));
2496 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2497 Selection::iterator next = i;
2500 /* note the negation of the delta for start */
2502 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2511 MidiRegionView::nudge_notes (bool forward)
2513 if (_selection.empty()) {
2517 /* pick a note as the point along the timeline to get the nudge distance.
2518 its not necessarily the earliest note, so we may want to pull the notes out
2519 into a vector and sort before using the first one.
2522 nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2524 nframes64_t distance;
2526 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2528 /* grid is off - use nudge distance */
2530 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2536 nframes64_t next_pos = ref_point;
2539 /* XXX need check on max_frames, but that needs max_frames64 or something */
2542 if (next_pos == 0) {
2548 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2549 distance = ref_point - next_pos;
2552 if (distance == 0) {
2556 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2562 start_diff_command (_("nudge"));
2564 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2565 Selection::iterator next = i;
2567 change_note_time (*i, delta, true);
2575 MidiRegionView::change_channel(uint8_t channel)
2577 start_diff_command(_("change channel"));
2578 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2579 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2587 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2589 if (_mouse_state == SelectTouchDragging) {
2590 note_selected (ev, true);
2593 show_verbose_canvas_cursor (ev->note ());
2597 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent* note)
2599 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2600 (*i)->hide_velocity ();
2603 trackview.editor().hide_verbose_canvas_cursor ();
2607 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2609 boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2611 display_model(msrc->model());
2615 MidiRegionView::set_frame_color()
2618 if (_selected && should_show_selection) {
2619 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2621 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2627 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2631 case FilterChannels:
2632 _force_channel = -1;
2635 _force_channel = mask;
2636 mask = 0xFFFF; // Show all notes as active (below)
2639 // Update notes for selection
2640 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2641 (*i)->on_channel_selection_change(mask);
2644 _last_channel_selection = mask;
2648 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2650 _model_name = model;
2651 _custom_device_mode = custom_device_mode;
2656 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2658 if (_selection.empty()) {
2662 PublicEditor& editor (trackview.editor());
2667 editor.get_cut_buffer().add (selection_as_cut_buffer());
2675 start_diff_command();
2677 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2683 diff_remove_note (*i);
2693 MidiRegionView::selection_as_cut_buffer () const
2697 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2698 NoteType* n = (*i)->note().get();
2699 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2702 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2709 MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
2715 start_diff_command (_("paste"));
2717 Evoral::MusicalTime beat_delta;
2718 Evoral::MusicalTime paste_pos_beats;
2719 Evoral::MusicalTime duration;
2720 Evoral::MusicalTime end_point = 0;
2722 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2723 paste_pos_beats = frames_to_beats (pos - _region->position());
2724 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2725 paste_pos_beats = 0;
2727 _selection.clear ();
2729 for (int n = 0; n < (int) times; ++n) {
2731 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2733 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2734 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2736 /* make all newly added notes selected */
2738 diff_add_note (copied_note, true);
2739 end_point = copied_note->end_time();
2742 paste_pos_beats += duration;
2745 /* if we pasted past the current end of the region, extend the region */
2747 nframes64_t end_frame = _region->position() + beats_to_frames (end_point);
2748 nframes64_t region_end = _region->position() + _region->length() - 1;
2750 if (end_frame > region_end) {
2752 trackview.session()->begin_reversible_command (_("paste"));
2754 _region->clear_history ();
2755 _region->set_length (end_frame, this);
2756 trackview.session()->add_command (new StatefulDiffCommand (_region));
2762 struct EventNoteTimeEarlyFirstComparator {
2763 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2764 return a->note()->time() < b->note()->time();
2769 MidiRegionView::time_sort_events ()
2771 if (!_sort_needed) {
2775 EventNoteTimeEarlyFirstComparator cmp;
2778 _sort_needed = false;
2782 MidiRegionView::goto_next_note ()
2784 // nframes64_t pos = -1;
2785 bool use_next = false;
2787 if (_events.back()->selected()) {
2791 time_sort_events ();
2793 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2794 if ((*i)->selected()) {
2797 } else if (use_next) {
2799 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2804 /* use the first one */
2806 unique_select (_events.front());
2811 MidiRegionView::goto_previous_note ()
2813 // nframes64_t pos = -1;
2814 bool use_next = false;
2816 if (_events.front()->selected()) {
2820 time_sort_events ();
2822 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2823 if ((*i)->selected()) {
2826 } else if (use_next) {
2828 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2833 /* use the last one */
2835 unique_select (*(_events.rbegin()));
2839 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2841 bool had_selected = false;
2843 time_sort_events ();
2845 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2846 if ((*i)->selected()) {
2847 selected.insert ((*i)->note());
2848 had_selected = true;
2852 if (allow_all_if_none_selected && !had_selected) {
2853 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2854 selected.insert ((*i)->note());
2860 MidiRegionView::update_ghost_note (double x, double y)
2866 nframes64_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2867 trackview.editor().snap_to (f);
2868 f -= _region->position ();
2871 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2876 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2878 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2879 _ghost_note->note()->set_length (length);
2880 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2882 update_note (_ghost_note);
2884 show_verbose_canvas_cursor (_ghost_note->note ());
2888 MidiRegionView::create_ghost_note (double x, double y)
2893 boost::shared_ptr<NoteType> g (new NoteType);
2894 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2895 update_ghost_note (x, y);
2896 _ghost_note->show ();
2901 show_verbose_canvas_cursor (_ghost_note->note ());
2905 MidiRegionView::snap_changed ()
2911 create_ghost_note (_last_ghost_x, _last_ghost_y);
2915 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
2918 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
2919 Evoral::midi_note_name (n->note()).c_str(),
2921 (int) n->velocity());
2922 trackview.editor().show_verbose_canvas_cursor_with (buf);
2926 MidiRegionView::drop_down_keys ()
2928 _mouse_state = None;
2932 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double x, double y)
2934 double note = midi_stream_view()->y_to_note(y);
2936 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2938 cerr << "Selecting by position\n";
2940 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
2942 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
2943 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
2944 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
2945 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
2950 bool add_mrv_selection = false;
2952 if (_selection.empty()) {
2953 add_mrv_selection = true;
2956 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
2957 if (_selection.insert (*i).second) {
2958 (*i)->set_selected (true);
2962 if (add_mrv_selection) {
2963 PublicEditor& editor (trackview.editor());
2964 editor.get_selection().add (this);
2969 MidiRegionView::color_handler ()
2971 RegionView::color_handler ();
2973 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2974 (*i)->set_selected ((*i)->selected()); // will change color
2977 /* XXX probably more to do here */
2981 MidiRegionView::enable_display (bool yn)
2983 RegionView::enable_display (yn);
2990 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
2992 if (_step_edit_cursor == 0) {
2993 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
2995 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
2996 _step_edit_cursor->property_y1() = 0;
2997 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
2998 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
2999 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3002 move_step_edit_cursor (pos);
3003 _step_edit_cursor->show ();
3007 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3009 _step_edit_cursor_position = pos;
3011 if (_step_edit_cursor) {
3012 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3013 _step_edit_cursor->property_x1() = pixel;
3014 set_step_edit_cursor_width (_step_edit_cursor_width);
3019 MidiRegionView::hide_step_edit_cursor ()
3021 if (_step_edit_cursor) {
3022 _step_edit_cursor->hide ();
3027 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3029 _step_edit_cursor_width = beats;
3031 if (_step_edit_cursor) {
3032 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));