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 _marked_for_selection.clear ();
1497 start_diff_command (_("step add"));
1498 diff_add_note (new_note, true, false);
1501 // last_step_edit_note = new_note;
1505 MidiRegionView::add_pgm_change(PCEvent& program, const string& displaytext)
1507 assert(program.time >= 0);
1509 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
1510 const double x = trackview.editor().frame_to_pixel(beats_to_frames(program.time));
1512 double height = midi_stream_view()->contents_height();
1514 boost::shared_ptr<CanvasProgramChange> pgm_change = boost::shared_ptr<CanvasProgramChange>(
1515 new CanvasProgramChange(*this, *group,
1520 _custom_device_mode,
1521 program.time, program.channel, program.value));
1523 // Show unless program change is beyond the region bounds
1524 if (program.time - _region->start() >= _region->length() || program.time < _region->start()) {
1530 _pgm_changes.push_back(pgm_change);
1534 MidiRegionView::get_patch_key_at(double time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1536 Evoral::Parameter bank_select_msb(MidiCCAutomation, channel, MIDI_CTL_MSB_BANK);
1537 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1539 if (msb_control != 0) {
1540 msb = int(msb_control->get_double(true, time));
1543 Evoral::Parameter bank_select_lsb(MidiCCAutomation, channel, MIDI_CTL_LSB_BANK);
1544 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1546 if (lsb_control != 0) {
1547 lsb = lsb_control->get_double(true, time);
1550 Evoral::Parameter program_change(MidiPgmChangeAutomation, channel, 0);
1551 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1552 double program_number = -1.0;
1553 if (program_control != 0) {
1554 program_number = program_control->get_double(true, time);
1557 key.msb = (int) floor(msb + 0.5);
1558 key.lsb = (int) floor(lsb + 0.5);
1559 key.program_number = (int) floor(program_number + 0.5);
1560 assert(key.is_sane());
1565 MidiRegionView::alter_program_change(PCEvent& old_program, const MIDI::Name::PatchPrimaryKey& new_patch)
1567 // TODO: Get the real event here and alter them at the original times
1568 Evoral::Parameter bank_select_msb(MidiCCAutomation, old_program.channel, MIDI_CTL_MSB_BANK);
1569 boost::shared_ptr<Evoral::Control> msb_control = _model->control(bank_select_msb);
1570 if (msb_control != 0) {
1571 msb_control->set_double(double(new_patch.msb), true, old_program.time);
1574 // TODO: Get the real event here and alter them at the original times
1575 Evoral::Parameter bank_select_lsb(MidiCCAutomation, old_program.channel, MIDI_CTL_LSB_BANK);
1576 boost::shared_ptr<Evoral::Control> lsb_control = _model->control(bank_select_lsb);
1577 if (lsb_control != 0) {
1578 lsb_control->set_double(double(new_patch.lsb), true, old_program.time);
1581 Evoral::Parameter program_change(MidiPgmChangeAutomation, old_program.channel, 0);
1582 boost::shared_ptr<Evoral::Control> program_control = _model->control(program_change);
1584 assert(program_control != 0);
1585 program_control->set_double(float(new_patch.program_number), true, old_program.time);
1587 _pgm_changes.clear ();
1588 display_program_changes (); // XXX would be nice to limit to just old_program.channel
1592 MidiRegionView::program_selected(CanvasProgramChange& program, const MIDI::Name::PatchPrimaryKey& new_patch)
1594 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1595 alter_program_change(program_change_event, new_patch);
1599 MidiRegionView::previous_program(CanvasProgramChange& program)
1601 if (program.program() < 127) {
1602 MIDI::Name::PatchPrimaryKey key;
1603 get_patch_key_at(program.event_time(), program.channel(), key);
1604 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1606 key.program_number++;
1607 alter_program_change(program_change_event, key);
1612 MidiRegionView::next_program(CanvasProgramChange& program)
1614 if (program.program() > 0) {
1615 MIDI::Name::PatchPrimaryKey key;
1616 get_patch_key_at(program.event_time(), program.channel(), key);
1617 PCEvent program_change_event(program.event_time(), program.program(), program.channel());
1619 key.program_number--;
1620 alter_program_change(program_change_event, key);
1625 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1627 if (_selection.empty()) {
1631 if (_selection.erase (cne) > 0) {
1632 cerr << "Erased a CNE from selection\n";
1637 MidiRegionView::delete_selection()
1639 if (_selection.empty()) {
1643 start_diff_command (_("delete selection"));
1645 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1646 if ((*i)->selected()) {
1647 _diff_command->remove((*i)->note());
1657 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1659 start_diff_command (_("delete note"));
1660 _diff_command->remove (n);
1663 trackview.editor().hide_verbose_canvas_cursor ();
1667 MidiRegionView::clear_selection_except(ArdourCanvas::CanvasNoteEvent* ev)
1669 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1670 if ((*i)->selected() && (*i) != ev) {
1671 (*i)->set_selected(false);
1672 (*i)->hide_velocity();
1680 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1682 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1685 Selection::iterator tmp = i;
1688 (*i)->set_selected (false);
1689 _selection.erase (i);
1698 /* don't bother with removing this regionview from the editor selection,
1699 since we're about to add another note, and thus put/keep this
1700 regionview in the editor selection.
1703 if (!ev->selected()) {
1704 add_to_selection (ev);
1709 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1711 uint8_t low_note = 127;
1712 uint8_t high_note = 0;
1713 MidiModel::Notes& notes (_model->notes());
1714 _optimization_iterator = _events.begin();
1720 if (extend && _selection.empty()) {
1726 /* scan existing selection to get note range */
1728 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1729 if ((*i)->note()->note() < low_note) {
1730 low_note = (*i)->note()->note();
1732 if ((*i)->note()->note() > high_note) {
1733 high_note = (*i)->note()->note();
1737 low_note = min (low_note, notenum);
1738 high_note = max (high_note, notenum);
1741 no_sound_notes = true;
1743 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1745 boost::shared_ptr<NoteType> note (*n);
1746 CanvasNoteEvent* cne;
1747 bool select = false;
1749 if (((1 << note->channel()) & channel_mask) != 0) {
1751 if ((note->note() >= low_note && note->note() <= high_note)) {
1754 } else if (note->note() == notenum) {
1760 if ((cne = find_canvas_note (note)) != 0) {
1761 // extend is false because we've taken care of it,
1762 // since it extends by time range, not pitch.
1763 note_selected (cne, add, false);
1767 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
1771 no_sound_notes = false;
1775 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
1777 MidiModel::Notes& notes (_model->notes());
1778 _optimization_iterator = _events.begin();
1780 for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1782 boost::shared_ptr<NoteType> note (*n);
1783 CanvasNoteEvent* cne;
1785 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
1786 if ((cne = find_canvas_note (note)) != 0) {
1787 if (cne->selected()) {
1788 note_deselected (cne);
1790 note_selected (cne, true, false);
1798 MidiRegionView::note_selected(ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
1801 clear_selection_except(ev);
1806 if (!ev->selected()) {
1807 add_to_selection (ev);
1811 /* find end of latest note selected, select all between that and the start of "ev" */
1813 Evoral::MusicalTime earliest = DBL_MAX;
1814 Evoral::MusicalTime latest = 0;
1816 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1817 if ((*i)->note()->end_time() > latest) {
1818 latest = (*i)->note()->end_time();
1820 if ((*i)->note()->time() < earliest) {
1821 earliest = (*i)->note()->time();
1825 if (ev->note()->end_time() > latest) {
1826 latest = ev->note()->end_time();
1829 if (ev->note()->time() < earliest) {
1830 earliest = ev->note()->time();
1833 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1835 /* find notes entirely within OR spanning the earliest..latest range */
1837 if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
1838 ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
1839 add_to_selection (*i);
1843 /* if events were guaranteed to be time sorted, we could do this.
1844 but as of sept 10th 2009, they no longer are.
1847 if ((*i)->note()->time() > latest) {
1856 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
1858 remove_from_selection (ev);
1862 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2)
1872 // TODO: Make this faster by storing the last updated selection rect, and only
1873 // adjusting things that are in the area that appears/disappeared.
1874 // We probably need a tree to be able to find events in O(log(n)) time.
1876 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1878 /* check if any corner of the note is inside the rect
1881 1) this is computing "touched by", not "contained by" the rect.
1882 2) this does not require that events be sorted in time.
1885 const double ix1 = (*i)->x1();
1886 const double ix2 = (*i)->x2();
1887 const double iy1 = (*i)->y1();
1888 const double iy2 = (*i)->y2();
1890 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1891 (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
1892 (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
1893 (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
1896 if (!(*i)->selected()) {
1897 add_to_selection (*i);
1899 } else if ((*i)->selected()) {
1900 // Not inside rectangle
1901 remove_from_selection (*i);
1907 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
1909 Selection::iterator i = _selection.find (ev);
1911 if (i != _selection.end()) {
1912 _selection.erase (i);
1915 ev->set_selected (false);
1916 ev->hide_velocity ();
1918 if (_selection.empty()) {
1919 PublicEditor& editor (trackview.editor());
1920 editor.get_selection().remove (this);
1925 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
1927 bool add_mrv_selection = false;
1929 if (_selection.empty()) {
1930 add_mrv_selection = true;
1933 if (_selection.insert (ev).second) {
1934 ev->set_selected (true);
1935 play_midi_note ((ev)->note());
1938 if (add_mrv_selection) {
1939 PublicEditor& editor (trackview.editor());
1940 editor.get_selection().add (this);
1945 MidiRegionView::move_selection(double dx, double dy)
1947 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1948 (*i)->move_event(dx, dy);
1953 MidiRegionView::note_dropped(CanvasNoteEvent *, double dt, int8_t dnote)
1955 assert (!_selection.empty());
1957 uint8_t lowest_note_in_selection = 127;
1958 uint8_t highest_note_in_selection = 0;
1959 uint8_t highest_note_difference = 0;
1961 // find highest and lowest notes first
1963 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1964 uint8_t pitch = (*i)->note()->note();
1965 lowest_note_in_selection = std::min(lowest_note_in_selection, pitch);
1966 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
1970 cerr << "dnote: " << (int) dnote << endl;
1971 cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
1972 << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
1973 cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
1974 << int(highest_note_in_selection) << endl;
1975 cerr << "selection size: " << _selection.size() << endl;
1976 cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
1979 // Make sure the note pitch does not exceed the MIDI standard range
1980 if (highest_note_in_selection + dnote > 127) {
1981 highest_note_difference = highest_note_in_selection - 127;
1984 start_diff_command(_("move notes"));
1986 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
1988 nframes64_t start_frames = beats_to_frames((*i)->note()->time());
1991 start_frames += snap_frame_to_frame(trackview.editor().pixel_to_frame(dt));
1993 start_frames -= snap_frame_to_frame(trackview.editor().pixel_to_frame(-dt));
1996 Evoral::MusicalTime new_time = frames_to_beats(start_frames);
2002 diff_add_change (*i, MidiModel::DiffCommand::StartTime, new_time);
2004 uint8_t original_pitch = (*i)->note()->note();
2005 uint8_t new_pitch = original_pitch + dnote - highest_note_difference;
2007 // keep notes in standard midi range
2008 clamp_to_0_127(new_pitch);
2010 // keep original pitch if note is dragged outside valid midi range
2011 if ((original_pitch != 0 && new_pitch == 0)
2012 || (original_pitch != 127 && new_pitch == 127)) {
2013 new_pitch = original_pitch;
2016 lowest_note_in_selection = std::min(lowest_note_in_selection, new_pitch);
2017 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2019 diff_add_change (*i, MidiModel::DiffCommand::NoteNumber, new_pitch);
2024 // care about notes being moved beyond the upper/lower bounds on the canvas
2025 if (lowest_note_in_selection < midi_stream_view()->lowest_note() ||
2026 highest_note_in_selection > midi_stream_view()->highest_note()) {
2027 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2032 MidiRegionView::snap_pixel_to_frame(double x)
2034 PublicEditor& editor = trackview.editor();
2035 // x is region relative, convert it to global absolute frames
2036 nframes64_t frame = editor.pixel_to_frame(x) + _region->position();
2037 editor.snap_to(frame);
2038 return frame - _region->position(); // convert back to region relative
2042 MidiRegionView::snap_frame_to_frame(nframes64_t x)
2044 PublicEditor& editor = trackview.editor();
2045 // x is region relative, convert it to global absolute frames
2046 nframes64_t frame = x + _region->position();
2047 editor.snap_to(frame);
2048 return frame - _region->position(); // convert back to region relative
2052 MidiRegionView::snap_to_pixel(double x)
2054 return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2058 MidiRegionView::get_position_pixels()
2060 nframes64_t region_frame = get_position();
2061 return trackview.editor().frame_to_pixel(region_frame);
2065 MidiRegionView::get_end_position_pixels()
2067 nframes64_t frame = get_position() + get_duration ();
2068 return trackview.editor().frame_to_pixel(frame);
2072 MidiRegionView::beats_to_frames(double beats) const
2074 return _time_converter.to(beats);
2078 MidiRegionView::frames_to_beats(nframes64_t frames) const
2080 return _time_converter.from(frames);
2084 MidiRegionView::begin_resizing (bool /*at_front*/)
2086 _resize_data.clear();
2088 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2089 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2091 // only insert CanvasNotes into the map
2093 NoteResizeData *resize_data = new NoteResizeData();
2094 resize_data->canvas_note = note;
2096 // create a new SimpleRect from the note which will be the resize preview
2097 SimpleRect *resize_rect = new SimpleRect(
2098 *group, note->x1(), note->y1(), note->x2(), note->y2());
2100 // calculate the colors: get the color settings
2101 uint32_t fill_color = UINT_RGBA_CHANGE_A(
2102 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2105 // make the resize preview notes more transparent and bright
2106 fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2108 // calculate color based on note velocity
2109 resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2110 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2114 resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2115 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2117 resize_data->resize_rect = resize_rect;
2118 _resize_data.push_back(resize_data);
2123 /** Update resizing notes while user drags.
2124 * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2125 * @param at_front which end of the note (true == note on, false == note off)
2126 * @param delta_x change in mouse position since the start of the drag
2127 * @param relative true if relative resizing is taking place, false if absolute resizing. This only makes
2128 * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2129 * amount of the drag. In non-relative mode, all selected notes are set to have the same start or end point
2130 * as the \a primary note.
2133 MidiRegionView::update_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2135 bool cursor_set = false;
2137 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2138 SimpleRect* resize_rect = (*i)->resize_rect;
2139 CanvasNote* canvas_note = (*i)->canvas_note;
2144 current_x = canvas_note->x1() + delta_x;
2146 current_x = primary->x1() + delta_x;
2150 current_x = canvas_note->x2() + delta_x;
2152 current_x = primary->x2() + delta_x;
2157 resize_rect->property_x1() = snap_to_pixel(current_x);
2158 resize_rect->property_x2() = canvas_note->x2();
2160 resize_rect->property_x2() = snap_to_pixel(current_x);
2161 resize_rect->property_x1() = canvas_note->x1();
2167 beats = snap_pixel_to_frame (current_x);
2168 beats = frames_to_beats (beats);
2173 if (beats < canvas_note->note()->end_time()) {
2174 len = canvas_note->note()->time() - beats;
2175 len += canvas_note->note()->length();
2180 if (beats >= canvas_note->note()->end_time()) {
2181 len = beats - canvas_note->note()->time();
2188 snprintf (buf, sizeof (buf), "%.3g beats", len);
2189 trackview.editor().show_verbose_canvas_cursor_with (buf);
2198 /** Finish resizing notes when the user releases the mouse button.
2199 * Parameters the same as for \a update_resizing().
2202 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNote* primary, bool at_front, double delta_x, bool relative)
2204 start_diff_command(_("resize notes"));
2206 for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2207 CanvasNote* canvas_note = (*i)->canvas_note;
2208 SimpleRect* resize_rect = (*i)->resize_rect;
2213 current_x = canvas_note->x1() + delta_x;
2215 current_x = primary->x1() + delta_x;
2219 current_x = canvas_note->x2() + delta_x;
2221 current_x = primary->x2() + delta_x;
2225 current_x = snap_pixel_to_frame (current_x);
2226 current_x = frames_to_beats (current_x);
2228 if (at_front && current_x < canvas_note->note()->end_time()) {
2229 diff_add_change (canvas_note, MidiModel::DiffCommand::StartTime, current_x);
2231 double len = canvas_note->note()->time() - current_x;
2232 len += canvas_note->note()->length();
2235 /* XXX convert to beats */
2236 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2241 double len = current_x - canvas_note->note()->time();
2244 /* XXX convert to beats */
2245 diff_add_change (canvas_note, MidiModel::DiffCommand::Length, len);
2253 _resize_data.clear();
2258 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2260 uint8_t new_velocity;
2263 new_velocity = event->note()->velocity() + velocity;
2264 clamp_to_0_127(new_velocity);
2266 new_velocity = velocity;
2269 event->set_selected (event->selected()); // change color
2271 diff_add_change (event, MidiModel::DiffCommand::Velocity, new_velocity);
2275 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2280 new_note = event->note()->note() + note;
2285 clamp_to_0_127 (new_note);
2286 diff_add_change (event, MidiModel::DiffCommand::NoteNumber, new_note);
2290 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2292 bool change_start = false;
2293 bool change_length = false;
2294 Evoral::MusicalTime new_start = 0;
2295 Evoral::MusicalTime new_length = 0;
2297 /* NOTE: the semantics of the two delta arguments are slightly subtle:
2299 front_delta: if positive - move the start of the note later in time (shortening it)
2300 if negative - move the start of the note earlier in time (lengthening it)
2302 end_delta: if positive - move the end of the note later in time (lengthening it)
2303 if negative - move the end of the note earlier in time (shortening it)
2307 if (front_delta < 0) {
2309 if (event->note()->time() < -front_delta) {
2312 new_start = event->note()->time() + front_delta; // moves earlier
2315 /* start moved toward zero, so move the end point out to where it used to be.
2316 Note that front_delta is negative, so this increases the length.
2319 new_length = event->note()->length() - front_delta;
2320 change_start = true;
2321 change_length = true;
2325 Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2327 if (new_pos < event->note()->end_time()) {
2328 new_start = event->note()->time() + front_delta;
2329 /* start moved toward the end, so move the end point back to where it used to be */
2330 new_length = event->note()->length() - front_delta;
2331 change_start = true;
2332 change_length = true;
2339 bool can_change = true;
2340 if (end_delta < 0) {
2341 if (event->note()->length() < -end_delta) {
2347 new_length = event->note()->length() + end_delta;
2348 change_length = true;
2353 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_start);
2356 if (change_length) {
2357 diff_add_change (event, MidiModel::DiffCommand::Length, new_length);
2362 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2364 Evoral::MusicalTime new_time;
2368 if (event->note()->time() < -delta) {
2371 new_time = event->note()->time() + delta;
2374 new_time = event->note()->time() + delta;
2380 diff_add_change (event, MidiModel::DiffCommand::StartTime, new_time);
2384 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2388 if (_selection.empty()) {
2403 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2404 if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2410 start_diff_command(_("change velocities"));
2412 for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2413 Selection::iterator next = i;
2415 change_note_velocity (*i, delta, true);
2421 if (!_selection.empty()) {
2423 snprintf (buf, sizeof (buf), "Vel %d",
2424 (int) (*_selection.begin())->note()->velocity());
2425 trackview.editor().show_verbose_canvas_cursor_with (buf);
2431 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2433 if (_selection.empty()) {
2450 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2452 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2456 if ((int8_t) (*i)->note()->note() + delta > 127) {
2463 start_diff_command (_("transpose"));
2465 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2466 Selection::iterator next = i;
2468 change_note_note (*i, delta, true);
2476 MidiRegionView::change_note_lengths (bool fine, bool shorter, bool start, bool end)
2478 Evoral::MusicalTime delta;
2483 /* grab the current grid distance */
2485 delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2487 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2488 cerr << "Grid type not available as beats - TO BE FIXED\n";
2497 start_diff_command (_("change note lengths"));
2499 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2500 Selection::iterator next = i;
2503 /* note the negation of the delta for start */
2505 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2514 MidiRegionView::nudge_notes (bool forward)
2516 if (_selection.empty()) {
2520 /* pick a note as the point along the timeline to get the nudge distance.
2521 its not necessarily the earliest note, so we may want to pull the notes out
2522 into a vector and sort before using the first one.
2525 nframes64_t ref_point = _region->position() + beats_to_frames ((*(_selection.begin()))->note()->time());
2527 nframes64_t distance;
2529 if (trackview.editor().snap_mode() == Editing::SnapOff) {
2531 /* grid is off - use nudge distance */
2533 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2539 nframes64_t next_pos = ref_point;
2542 /* XXX need check on max_frames, but that needs max_frames64 or something */
2545 if (next_pos == 0) {
2551 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2552 distance = ref_point - next_pos;
2555 if (distance == 0) {
2559 Evoral::MusicalTime delta = frames_to_beats (fabs (distance));
2565 start_diff_command (_("nudge"));
2567 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2568 Selection::iterator next = i;
2570 change_note_time (*i, delta, true);
2578 MidiRegionView::change_channel(uint8_t channel)
2580 start_diff_command(_("change channel"));
2581 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2582 diff_add_change (*i, MidiModel::DiffCommand::Channel, channel);
2590 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2592 if (_mouse_state == SelectTouchDragging) {
2593 note_selected (ev, true);
2596 show_verbose_canvas_cursor (ev->note ());
2600 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent* note)
2602 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2603 (*i)->hide_velocity ();
2606 trackview.editor().hide_verbose_canvas_cursor ();
2610 MidiRegionView::switch_source(boost::shared_ptr<Source> src)
2612 boost::shared_ptr<MidiSource> msrc = boost::dynamic_pointer_cast<MidiSource>(src);
2614 display_model(msrc->model());
2618 MidiRegionView::set_frame_color()
2621 if (_selected && should_show_selection) {
2622 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
2624 frame->property_fill_color_rgba() = ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
2630 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
2634 case FilterChannels:
2635 _force_channel = -1;
2638 _force_channel = mask;
2639 mask = 0xFFFF; // Show all notes as active (below)
2642 // Update notes for selection
2643 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2644 (*i)->on_channel_selection_change(mask);
2647 _last_channel_selection = mask;
2651 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
2653 _model_name = model;
2654 _custom_device_mode = custom_device_mode;
2659 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
2661 if (_selection.empty()) {
2665 PublicEditor& editor (trackview.editor());
2670 editor.get_cut_buffer().add (selection_as_cut_buffer());
2678 start_diff_command();
2680 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2686 diff_remove_note (*i);
2696 MidiRegionView::selection_as_cut_buffer () const
2700 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2701 NoteType* n = (*i)->note().get();
2702 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
2705 MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
2712 MidiRegionView::paste (nframes64_t pos, float times, const MidiCutBuffer& mcb)
2718 start_diff_command (_("paste"));
2720 Evoral::MusicalTime beat_delta;
2721 Evoral::MusicalTime paste_pos_beats;
2722 Evoral::MusicalTime duration;
2723 Evoral::MusicalTime end_point = 0;
2725 duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
2726 paste_pos_beats = frames_to_beats (pos - _region->position());
2727 beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
2728 paste_pos_beats = 0;
2730 _selection.clear ();
2732 for (int n = 0; n < (int) times; ++n) {
2734 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
2736 boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
2737 copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
2739 /* make all newly added notes selected */
2741 diff_add_note (copied_note, true);
2742 end_point = copied_note->end_time();
2745 paste_pos_beats += duration;
2748 /* if we pasted past the current end of the region, extend the region */
2750 nframes64_t end_frame = _region->position() + beats_to_frames (end_point);
2751 nframes64_t region_end = _region->position() + _region->length() - 1;
2753 if (end_frame > region_end) {
2755 trackview.session()->begin_reversible_command (_("paste"));
2757 _region->clear_history ();
2758 _region->set_length (end_frame, this);
2759 trackview.session()->add_command (new StatefulDiffCommand (_region));
2765 struct EventNoteTimeEarlyFirstComparator {
2766 bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
2767 return a->note()->time() < b->note()->time();
2772 MidiRegionView::time_sort_events ()
2774 if (!_sort_needed) {
2778 EventNoteTimeEarlyFirstComparator cmp;
2781 _sort_needed = false;
2785 MidiRegionView::goto_next_note ()
2787 // nframes64_t pos = -1;
2788 bool use_next = false;
2790 if (_events.back()->selected()) {
2794 time_sort_events ();
2796 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2797 if ((*i)->selected()) {
2800 } else if (use_next) {
2802 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2807 /* use the first one */
2809 unique_select (_events.front());
2814 MidiRegionView::goto_previous_note ()
2816 // nframes64_t pos = -1;
2817 bool use_next = false;
2819 if (_events.front()->selected()) {
2823 time_sort_events ();
2825 for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
2826 if ((*i)->selected()) {
2829 } else if (use_next) {
2831 // pos = _region->position() + beats_to_frames ((*i)->note()->time());
2836 /* use the last one */
2838 unique_select (*(_events.rbegin()));
2842 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
2844 bool had_selected = false;
2846 time_sort_events ();
2848 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2849 if ((*i)->selected()) {
2850 selected.insert ((*i)->note());
2851 had_selected = true;
2855 if (allow_all_if_none_selected && !had_selected) {
2856 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2857 selected.insert ((*i)->note());
2863 MidiRegionView::update_ghost_note (double x, double y)
2869 nframes64_t f = trackview.editor().pixel_to_frame (x) + _region->position ();
2870 trackview.editor().snap_to (f);
2871 f -= _region->position ();
2874 Evoral::MusicalTime beats = trackview.editor().get_grid_type_as_beats (success, f);
2879 double length = frames_to_beats (snap_frame_to_frame (f + beats_to_frames (beats)) - f);
2881 _ghost_note->note()->set_time (frames_to_beats (f + _region->start()));
2882 _ghost_note->note()->set_length (length);
2883 _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
2885 update_note (_ghost_note);
2887 show_verbose_canvas_cursor (_ghost_note->note ());
2891 MidiRegionView::create_ghost_note (double x, double y)
2896 boost::shared_ptr<NoteType> g (new NoteType);
2897 _ghost_note = new NoEventCanvasNote (*this, *group, g);
2898 update_ghost_note (x, y);
2899 _ghost_note->show ();
2904 show_verbose_canvas_cursor (_ghost_note->note ());
2908 MidiRegionView::snap_changed ()
2914 create_ghost_note (_last_ghost_x, _last_ghost_y);
2918 MidiRegionView::show_verbose_canvas_cursor (boost::shared_ptr<NoteType> n) const
2921 snprintf (buf, sizeof (buf), "%s (%d)\nVel %d",
2922 Evoral::midi_note_name (n->note()).c_str(),
2924 (int) n->velocity());
2925 trackview.editor().show_verbose_canvas_cursor_with (buf);
2929 MidiRegionView::drop_down_keys ()
2931 _mouse_state = None;
2935 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double x, double y)
2937 double note = midi_stream_view()->y_to_note(y);
2939 MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2941 cerr << "Selecting by position\n";
2943 uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
2945 if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
2946 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
2947 } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
2948 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
2953 bool add_mrv_selection = false;
2955 if (_selection.empty()) {
2956 add_mrv_selection = true;
2959 for (Events::iterator i = e.begin(); i != e.end(); ++i) {
2960 if (_selection.insert (*i).second) {
2961 (*i)->set_selected (true);
2965 if (add_mrv_selection) {
2966 PublicEditor& editor (trackview.editor());
2967 editor.get_selection().add (this);
2972 MidiRegionView::color_handler ()
2974 RegionView::color_handler ();
2976 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2977 (*i)->set_selected ((*i)->selected()); // will change color
2980 /* XXX probably more to do here */
2984 MidiRegionView::enable_display (bool yn)
2986 RegionView::enable_display (yn);
2993 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
2995 if (_step_edit_cursor == 0) {
2996 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
2998 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
2999 _step_edit_cursor->property_y1() = 0;
3000 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3001 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3002 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3005 move_step_edit_cursor (pos);
3006 _step_edit_cursor->show ();
3010 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3012 _step_edit_cursor_position = pos;
3014 if (_step_edit_cursor) {
3015 double pixel = trackview.editor().frame_to_pixel (beats_to_frames (pos));
3016 _step_edit_cursor->property_x1() = pixel;
3017 set_step_edit_cursor_width (_step_edit_cursor_width);
3022 MidiRegionView::hide_step_edit_cursor ()
3024 if (_step_edit_cursor) {
3025 _step_edit_cursor->hide ();
3030 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3032 _step_edit_cursor_width = beats;
3034 if (_step_edit_cursor) {
3035 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (beats_to_frames (beats));