fix drawing of zero-length notes
[ardour.git] / gtk2_ardour / midi_region_view.cc
1 /*
2  * Copyright (C) 2006-2016 David Robillard <d@drobilla.net>
3  * Copyright (C) 2007-2012 Carl Hetherington <carl@carlh.net>
4  * Copyright (C) 2007-2018 Paul Davis <paul@linuxaudiosystems.com>
5  * Copyright (C) 2008-2012 Hans Baier <hansfbaier@googlemail.com>
6  * Copyright (C) 2013-2017 John Emmas <john@creativepost.co.uk>
7  * Copyright (C) 2014-2017 Nick Mainsbridge <mainsbridge@gmail.com>
8  * Copyright (C) 2014-2018 Ben Loftis <ben@harrisonconsoles.com>
9  * Copyright (C) 2014-2019 Robin Gareus <robin@gareus.org>
10  * Copyright (C) 2015-2016 Tim Mayberry <mojofunk@gmail.com>
11  * Copyright (C) 2015-2017 AndrĂ© Nusser <andre.nusser@googlemail.com>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License along
24  * with this program; if not, write to the Free Software Foundation, Inc.,
25  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26  */
27
28 #include <cmath>
29 #include <algorithm>
30 #include <ostream>
31
32 #include <gtkmm.h>
33
34 #include "gtkmm2ext/gtk_ui.h"
35
36 #include <sigc++/signal.h>
37
38 #include "midi++/midnam_patch.h"
39
40 #include "pbd/memento_command.h"
41 #include "pbd/stateful_diff_command.h"
42
43 #include "ardour/midi_model.h"
44 #include "ardour/midi_playlist.h"
45 #include "ardour/midi_region.h"
46 #include "ardour/midi_source.h"
47 #include "ardour/midi_track.h"
48 #include "ardour/operations.h"
49 #include "ardour/session.h"
50
51 #include "evoral/Parameter.h"
52 #include "evoral/Event.h"
53 #include "evoral/Control.h"
54 #include "evoral/midi_util.h"
55
56 #include "canvas/debug.h"
57 #include "canvas/text.h"
58
59 #include "automation_region_view.h"
60 #include "automation_time_axis.h"
61 #include "control_point.h"
62 #include "debug.h"
63 #include "editor.h"
64 #include "editor_drag.h"
65 #include "ghostregion.h"
66 #include "gui_thread.h"
67 #include "item_counts.h"
68 #include "keyboard.h"
69 #include "midi_channel_dialog.h"
70 #include "midi_cut_buffer.h"
71 #include "midi_list_editor.h"
72 #include "midi_region_view.h"
73 #include "midi_streamview.h"
74 #include "midi_time_axis.h"
75 #include "midi_util.h"
76 #include "midi_velocity_dialog.h"
77 #include "mouse_cursors.h"
78 #include "note_player.h"
79 #include "paste_context.h"
80 #include "public_editor.h"
81 #include "route_time_axis.h"
82 #include "rgb_macros.h"
83 #include "selection.h"
84 #include "streamview.h"
85 #include "patch_change_dialog.h"
86 #include "verbose_cursor.h"
87 #include "note.h"
88 #include "hit.h"
89 #include "patch_change.h"
90 #include "sys_ex.h"
91 #include "ui_config.h"
92
93 #include "pbd/i18n.h"
94
95 using namespace ARDOUR;
96 using namespace PBD;
97 using namespace Editing;
98 using namespace std;
99 using Gtkmm2ext::Keyboard;
100
101 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
102
103 MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
104                                 RouteTimeAxisView&            tv,
105                                 boost::shared_ptr<MidiRegion> r,
106                                 double                        spu,
107                                 uint32_t                      basic_color)
108         : RegionView (parent, tv, r, spu, basic_color)
109         , _current_range_min(0)
110         , _current_range_max(0)
111         , _region_relative_time_converter(r->session().tempo_map(), r->position())
112         , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
113         , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
114         , _active_notes(0)
115         , _note_group (new ArdourCanvas::Container (group))
116         , _note_diff_command (0)
117         , _ghost_note(0)
118         , _step_edit_cursor (0)
119         , _step_edit_cursor_width (1.0)
120         , _step_edit_cursor_position (0.0)
121         , _channel_selection_scoped_note (0)
122         , _mouse_state(None)
123         , _pressed_button(0)
124         , _optimization_iterator (_events.end())
125         , _list_editor (0)
126         , _no_sound_notes (false)
127         , _last_display_zoom (0)
128         , _last_event_x (0)
129         , _last_event_y (0)
130         , _grabbed_keyboard (false)
131         , _entered (false)
132         , _entered_note (0)
133         , _mouse_changed_selection (false)
134 {
135         CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
136
137         _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
138         _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
139
140         _note_group->raise_to_top();
141         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
142
143         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
144         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MidiRegionView::parameter_changed));
145
146         connect_to_diskstream ();
147 }
148
149 MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
150                                 RouteTimeAxisView&            tv,
151                                 boost::shared_ptr<MidiRegion> r,
152                                 double                        spu,
153                                 uint32_t                      basic_color,
154                                 bool                          recording,
155                                 TimeAxisViewItem::Visibility  visibility)
156         : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
157         , _current_range_min(0)
158         , _current_range_max(0)
159         , _region_relative_time_converter(r->session().tempo_map(), r->position())
160         , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
161         , _region_relative_time_converter_double(r->session().tempo_map(), r->position())
162         , _active_notes(0)
163         , _note_group (new ArdourCanvas::Container (group))
164         , _note_diff_command (0)
165         , _ghost_note(0)
166         , _step_edit_cursor (0)
167         , _step_edit_cursor_width (1.0)
168         , _step_edit_cursor_position (0.0)
169         , _channel_selection_scoped_note (0)
170         , _mouse_state(None)
171         , _pressed_button(0)
172         , _optimization_iterator (_events.end())
173         , _list_editor (0)
174         , _no_sound_notes (false)
175         , _last_display_zoom (0)
176         , _last_event_x (0)
177         , _last_event_y (0)
178         , _grabbed_keyboard (false)
179         , _entered (false)
180         , _entered_note (0)
181         , _mouse_changed_selection (false)
182 {
183         CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
184
185         _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
186         _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
187
188         _note_group->raise_to_top();
189
190         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
191
192         connect_to_diskstream ();
193 }
194
195 void
196 MidiRegionView::parameter_changed (std::string const & p)
197 {
198         if (p == "display-first-midi-bank-as-zero") {
199                 if (_enable_display) {
200                         redisplay_model();
201                 }
202         } else if (p == "color-regions-using-track-color") {
203                 set_colors ();
204         } else if (p == "use-note-color-for-velocity") {
205                 color_handler ();
206         }
207 }
208
209 MidiRegionView::MidiRegionView (const MidiRegionView& other)
210         : sigc::trackable(other)
211         , RegionView (other)
212         , _current_range_min(0)
213         , _current_range_max(0)
214         , _region_relative_time_converter(other.region_relative_time_converter())
215         , _source_relative_time_converter(other.source_relative_time_converter())
216         , _region_relative_time_converter_double(other.region_relative_time_converter_double())
217         , _active_notes(0)
218         , _note_group (new ArdourCanvas::Container (get_canvas_group()))
219         , _note_diff_command (0)
220         , _ghost_note(0)
221         , _step_edit_cursor (0)
222         , _step_edit_cursor_width (1.0)
223         , _step_edit_cursor_position (0.0)
224         , _channel_selection_scoped_note (0)
225         , _mouse_state(None)
226         , _pressed_button(0)
227         , _optimization_iterator (_events.end())
228         , _list_editor (0)
229         , _no_sound_notes (false)
230         , _last_display_zoom (0)
231         , _last_event_x (0)
232         , _last_event_y (0)
233         , _grabbed_keyboard (false)
234         , _entered (false)
235         , _entered_note (0)
236         , _mouse_changed_selection (false)
237 {
238         init (false);
239 }
240
241 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
242         : RegionView (other, boost::shared_ptr<Region> (region))
243         , _current_range_min(0)
244         , _current_range_max(0)
245         , _region_relative_time_converter(other.region_relative_time_converter())
246         , _source_relative_time_converter(other.source_relative_time_converter())
247         , _region_relative_time_converter_double(other.region_relative_time_converter_double())
248         , _active_notes(0)
249         , _note_group (new ArdourCanvas::Container (get_canvas_group()))
250         , _note_diff_command (0)
251         , _ghost_note(0)
252         , _step_edit_cursor (0)
253         , _step_edit_cursor_width (1.0)
254         , _step_edit_cursor_position (0.0)
255         , _channel_selection_scoped_note (0)
256         , _mouse_state(None)
257         , _pressed_button(0)
258         , _optimization_iterator (_events.end())
259         , _list_editor (0)
260         , _no_sound_notes (false)
261         , _last_display_zoom (0)
262         , _last_event_x (0)
263         , _last_event_y (0)
264         , _grabbed_keyboard (false)
265         , _entered (false)
266         , _entered_note (0)
267         , _mouse_changed_selection (false)
268 {
269         init (true);
270 }
271
272 void
273 MidiRegionView::init (bool wfd)
274 {
275         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
276
277         if (wfd) {
278                 Glib::Threads::Mutex::Lock lm(midi_region()->midi_source(0)->mutex());
279                 midi_region()->midi_source(0)->load_model(lm);
280         }
281
282         _model = midi_region()->midi_source(0)->model();
283         _enable_display = false;
284         fill_color_name = "midi frame base";
285
286         RegionView::init (false);
287
288         //set_height (trackview.current_height());
289
290         region_muted ();
291         region_sync_changed ();
292         region_resized (ARDOUR::bounds_change);
293         //region_locked ();
294
295         set_colors ();
296
297         _enable_display = true;
298         if (_model) {
299                 if (wfd) {
300                         display_model (_model);
301                 }
302         }
303
304         reset_width_dependent_items (_pixel_width);
305
306         group->raise_to_top();
307
308         midi_view()->midi_track()->playback_filter().ChannelModeChanged.connect (_channel_mode_changed_connection, invalidator (*this),
309                                                                        boost::bind (&MidiRegionView::midi_channel_mode_changed, this),
310                                                                        gui_context ());
311
312         instrument_info().Changed.connect (_instrument_changed_connection, invalidator (*this),
313                                            boost::bind (&MidiRegionView::instrument_settings_changed, this), gui_context());
314
315         trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
316                                                boost::bind (&MidiRegionView::snap_changed, this),
317                                                gui_context());
318
319         trackview.editor().MouseModeChanged.connect(_mouse_mode_connection, invalidator (*this),
320                                                     boost::bind (&MidiRegionView::mouse_mode_changed, this),
321                                                     gui_context ());
322
323         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
324         UIConfiguration::instance().ParameterChanged.connect (sigc::mem_fun (*this, &MidiRegionView::parameter_changed));
325         connect_to_diskstream ();
326 }
327
328 InstrumentInfo&
329 MidiRegionView::instrument_info () const
330 {
331         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
332         return route_ui->route()->instrument_info();
333 }
334
335 const boost::shared_ptr<ARDOUR::MidiRegion>
336 MidiRegionView::midi_region() const
337 {
338         return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
339 }
340
341 void
342 MidiRegionView::connect_to_diskstream ()
343 {
344         midi_view()->midi_track()->DataRecorded.connect(
345                 *this, invalidator(*this),
346                 boost::bind (&MidiRegionView::data_recorded, this, _1),
347                 gui_context());
348 }
349
350 bool
351 MidiRegionView::canvas_group_event(GdkEvent* ev)
352 {
353         if (in_destructor || _recregion) {
354                 return false;
355         }
356
357         if (!trackview.editor().internal_editing()) {
358                 // not in internal edit mode, so just act like a normal region
359                 return RegionView::canvas_group_event (ev);
360         }
361
362         //For now, move the snapped cursor aside so it doesn't bother you during internal editing
363         //trackview.editor().set_snapped_cursor_position(_region->position());
364
365         bool r;
366
367         switch (ev->type) {
368         case GDK_ENTER_NOTIFY:
369                 _last_event_x = ev->crossing.x;
370                 _last_event_y = ev->crossing.y;
371                 enter_notify(&ev->crossing);
372                 // set entered_regionview (among other things)
373                 return RegionView::canvas_group_event (ev);
374
375         case GDK_LEAVE_NOTIFY:
376                 _last_event_x = ev->crossing.x;
377                 _last_event_y = ev->crossing.y;
378                 leave_notify(&ev->crossing);
379                 // reset entered_regionview (among other things)
380                 return RegionView::canvas_group_event (ev);
381
382         case GDK_SCROLL:
383                 if (scroll (&ev->scroll)) {
384                         return true;
385                 }
386                 break;
387
388         case GDK_KEY_PRESS:
389                 return key_press (&ev->key);
390
391         case GDK_KEY_RELEASE:
392                 return key_release (&ev->key);
393
394         case GDK_BUTTON_PRESS:
395                 return button_press (&ev->button);
396
397         case GDK_BUTTON_RELEASE:
398                 r = button_release (&ev->button);
399                 return r;
400
401         case GDK_MOTION_NOTIFY:
402                 _last_event_x = ev->motion.x;
403                 _last_event_y = ev->motion.y;
404                 return motion (&ev->motion);
405
406         default:
407                 break;
408         }
409
410         return RegionView::canvas_group_event (ev);
411 }
412
413 bool
414 MidiRegionView::enter_notify (GdkEventCrossing* ev)
415 {
416         enter_internal (ev->state);
417
418         _entered = true;
419         return false;
420 }
421
422 bool
423 MidiRegionView::leave_notify (GdkEventCrossing*)
424 {
425         leave_internal();
426
427         _entered = false;
428         return false;
429 }
430
431 void
432 MidiRegionView::mouse_mode_changed ()
433 {
434         // Adjust frame colour (become more transparent for internal tools)
435         set_frame_color();
436
437         if (_entered) {
438                 if (!trackview.editor().internal_editing()) {
439                         /* Switched out of internal editing mode while entered.
440                         Only necessary for leave as a mouse_mode_change over a region
441                         automatically triggers an enter event. */
442                         leave_internal();
443                 }
444                 else if (trackview.editor().current_mouse_mode() == MouseContent) {
445                         // hide cursor and ghost note after changing to internal edit mode
446                         remove_ghost_note ();
447
448                         /* XXX This is problematic as the function is executed for every region
449                         and only for one region _entered_note can be true. Still it's
450                         necessary as to hide the verbose cursor when we're changing from
451                         draw mode to internal edit mode. These lines are the reason why
452                         in some situations no verbose cursor is shown when we enter internal
453                         edit mode over a note. */
454                         if (!_entered_note) {
455                                 hide_verbose_cursor ();
456                         }
457                 }
458         }
459 }
460
461 void
462 MidiRegionView::enter_internal (uint32_t state)
463 {
464         if (trackview.editor().current_mouse_mode() == MouseDraw && _mouse_state != AddDragging) {
465                 // Show ghost note under pencil
466                 create_ghost_note(_last_event_x, _last_event_y, state);
467         }
468
469         if (!_selection.empty()) {
470                 // Grab keyboard for moving selected notes with arrow keys
471                 Keyboard::magic_widget_grab_focus();
472                 _grabbed_keyboard = true;
473         }
474
475         // Lower frame handles below notes so they don't steal events
476         if (frame_handle_start) {
477                 frame_handle_start->lower_to_bottom();
478         }
479         if (frame_handle_end) {
480                 frame_handle_end->lower_to_bottom();
481         }
482 }
483
484 void
485 MidiRegionView::leave_internal()
486 {
487         hide_verbose_cursor ();
488         remove_ghost_note ();
489         _entered_note = 0;
490
491         if (_grabbed_keyboard) {
492                 Keyboard::magic_widget_drop_focus();
493                 _grabbed_keyboard = false;
494         }
495
496         // Raise frame handles above notes so they catch events
497         if (frame_handle_start) {
498                 frame_handle_start->raise_to_top();
499         }
500         if (frame_handle_end) {
501                 frame_handle_end->raise_to_top();
502         }
503 }
504
505 bool
506 MidiRegionView::button_press (GdkEventButton* ev)
507 {
508         if (ev->button != 1) {
509                 return false;
510         }
511
512         Editor* editor = dynamic_cast<Editor *> (&trackview.editor());
513         MouseMode m = editor->current_mouse_mode();
514
515         if (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
516                 _press_cursor_ctx = CursorContext::create(*editor, editor->cursors()->midi_pencil);
517         }
518
519         if (_mouse_state != SelectTouchDragging) {
520
521                 _pressed_button = ev->button;
522
523                 if (m == MouseDraw || (m == MouseContent && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()))) {
524
525                         if (midi_view()->note_mode() == Percussive) {
526                                 editor->drags()->set (new HitCreateDrag (dynamic_cast<Editor *> (editor), group, this), (GdkEvent *) ev);
527                         } else {
528                                 editor->drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (editor), group, this), (GdkEvent *) ev);
529                         }
530
531                         _mouse_state = AddDragging;
532                         remove_ghost_note ();
533                         hide_verbose_cursor ();
534                 } else {
535                         _mouse_state = Pressed;
536                 }
537
538                 return true;
539         }
540
541         _pressed_button = ev->button;
542         _mouse_changed_selection = false;
543
544         return true;
545 }
546
547 bool
548 MidiRegionView::button_release (GdkEventButton* ev)
549 {
550         double event_x, event_y;
551
552         if (ev->button != 1) {
553                 return false;
554         }
555
556         event_x = ev->x;
557         event_y = ev->y;
558
559         group->canvas_to_item (event_x, event_y);
560         group->ungrab ();
561
562         PublicEditor& editor = trackview.editor ();
563
564         _press_cursor_ctx.reset();
565
566         switch (_mouse_state) {
567         case Pressed: // Clicked
568
569                 switch (editor.current_mouse_mode()) {
570                 case MouseRange:
571                         /* no motion occurred - simple click */
572                         clear_editor_note_selection ();
573                         _mouse_changed_selection = true;
574                         break;
575
576                 case MouseContent:
577                 case MouseTimeFX:
578                         {
579                                 _mouse_changed_selection = true;
580                                 clear_editor_note_selection ();
581
582                                 break;
583                         }
584                 case MouseDraw:
585                         break;
586
587                 default:
588                         break;
589                 }
590
591                 _mouse_state = None;
592                 break;
593
594         case AddDragging:
595                 /* Don't a ghost note when we added a note - wait until motion to avoid visual confusion.
596                    we don't want one when we were drag-selecting either. */
597         case SelectRectDragging:
598                 editor.drags()->end_grab ((GdkEvent *) ev);
599                 _mouse_state = None;
600                 break;
601
602
603         default:
604                 break;
605         }
606
607         if (_mouse_changed_selection) {
608                 trackview.editor().begin_reversible_selection_op (X_("Mouse Selection Change"));
609                 trackview.editor().commit_reversible_selection_op ();
610         }
611
612         return false;
613 }
614
615 bool
616 MidiRegionView::motion (GdkEventMotion* ev)
617 {
618         PublicEditor& editor = trackview.editor ();
619
620         if (!_entered_note) {
621
622                 if (_mouse_state == AddDragging) {
623                         if (_ghost_note) {
624                                 remove_ghost_note ();
625                         }
626
627                 } else if (!_ghost_note && editor.current_mouse_mode() == MouseContent &&
628                     Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier()) &&
629                     _mouse_state != AddDragging) {
630
631                         create_ghost_note (ev->x, ev->y, ev->state);
632
633                 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent &&
634                            Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
635
636                         update_ghost_note (ev->x, ev->y, ev->state);
637
638                 } else if (_ghost_note && editor.current_mouse_mode() == MouseContent) {
639
640                         remove_ghost_note ();
641                         hide_verbose_cursor ();
642
643                 } else if (editor.current_mouse_mode() == MouseDraw) {
644
645                         if (_ghost_note) {
646                                 update_ghost_note (ev->x, ev->y, ev->state);
647                         }
648                         else {
649                                 create_ghost_note (ev->x, ev->y, ev->state);
650                         }
651                 }
652         }
653
654         /* any motion immediately hides velocity text that may have been visible */
655
656         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
657                 (*i)->hide_velocity ();
658         }
659
660         switch (_mouse_state) {
661         case Pressed:
662
663                 if (_pressed_button == 1) {
664
665                         MouseMode m = editor.current_mouse_mode();
666
667                         if (m == MouseContent && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
668                                 editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
669                                 if (!Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
670                                         clear_editor_note_selection ();
671                                         _mouse_changed_selection = true;
672                                 }
673                                 _mouse_state = SelectRectDragging;
674                                 return true;
675                         } else if (m == MouseRange) {
676                                 editor.drags()->set (new MidiVerticalSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
677                                 _mouse_state = SelectVerticalDragging;
678                                 return true;
679                         }
680                 }
681
682                 return false;
683
684         case SelectRectDragging:
685         case SelectVerticalDragging:
686         case AddDragging:
687                 editor.drags()->motion_handler ((GdkEvent *) ev, false);
688                 break;
689
690         case SelectTouchDragging:
691                 return false;
692
693         default:
694                 break;
695
696         }
697
698         //let RegionView do it's thing.  drags are handled in here
699         return RegionView::canvas_group_event ((GdkEvent *) ev);
700 }
701
702
703 bool
704 MidiRegionView::scroll (GdkEventScroll* ev)
705 {
706         if (trackview.editor().drags()->active()) {
707                 return false;
708         }
709         if (_selection.empty()) {
710                 return false;
711         }
712
713         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier) ||
714             Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
715                 /* XXX: bit of a hack; allow PrimaryModifier and TertiaryModifier scroll
716                  * through so that it still works for navigation.
717                 */
718                 return false;
719         }
720
721         hide_verbose_cursor ();
722
723         bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
724         Keyboard::ModifierMask mask_together(Keyboard::PrimaryModifier|Keyboard::TertiaryModifier);
725         bool together = Keyboard::modifier_state_contains (ev->state, mask_together);
726
727         if (ev->direction == GDK_SCROLL_UP) {
728                 change_velocities (true, fine, false, together);
729         } else if (ev->direction == GDK_SCROLL_DOWN) {
730                 change_velocities (false, fine, false, together);
731         } else {
732                 /* left, right: we don't use them */
733                 return false;
734         }
735
736         return true;
737 }
738
739 bool
740 MidiRegionView::key_press (GdkEventKey* ev)
741 {
742         /* since GTK bindings are generally activated on press, and since
743            detectable auto-repeat is the name of the game and only sends
744            repeated presses, carry out key actions at key press, not release.
745         */
746         bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
747
748         if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
749
750                 if (_mouse_state != AddDragging) {
751                         _mouse_state = SelectTouchDragging;
752                 }
753
754                 return true;
755
756         } else if (ev->keyval == GDK_Escape && unmodified) {
757                 clear_editor_note_selection ();
758                 _mouse_state = None;
759
760         } else if (ev->keyval == GDK_comma || ev->keyval == GDK_period) {
761
762                 bool start = (ev->keyval == GDK_comma);
763                 bool end = (ev->keyval == GDK_period);
764                 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
765                 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
766
767                 change_note_lengths (fine, shorter, Temporal::Beats(), start, end);
768
769                 return true;
770
771         } else if ((ev->keyval == GDK_BackSpace || ev->keyval == GDK_Delete) && unmodified) {
772
773                 if (_selection.empty()) {
774                         return false;
775                 }
776
777                 delete_selection();
778                 return true;
779
780         } else if (ev->keyval == GDK_Tab || ev->keyval == GDK_ISO_Left_Tab) {
781
782                 trackview.editor().begin_reversible_selection_op (X_("Select Adjacent Note"));
783
784                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
785                         goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
786                 } else {
787                         goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
788                 }
789
790                 trackview.editor().commit_reversible_selection_op();
791
792                 return true;
793
794         } else if (ev->keyval == GDK_Up) {
795
796                 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
797                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
798                 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
799
800                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
801                         change_velocities (true, fine, allow_smush, together);
802                 } else {
803                         transpose (true, fine, allow_smush);
804                 }
805                 return true;
806
807         } else if (ev->keyval == GDK_Down) {
808
809                 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
810                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
811                 bool together = Keyboard::modifier_state_contains (ev->state, Keyboard::Level4Modifier);
812
813                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
814                         change_velocities (false, fine, allow_smush, together);
815                 } else {
816                         transpose (false, fine, allow_smush);
817                 }
818                 return true;
819
820         } else if (ev->keyval == GDK_Left) {
821
822                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
823                 nudge_notes (false, fine);
824                 return true;
825
826         } else if (ev->keyval == GDK_Right) {
827
828                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
829                 nudge_notes (true, fine);
830                 return true;
831
832         } else if (ev->keyval == GDK_c && unmodified) {
833                 channel_edit ();
834                 return true;
835
836         } else if (ev->keyval == GDK_v && unmodified) {
837                 velocity_edit ();
838                 return true;
839         }
840
841         return false;
842 }
843
844 bool
845 MidiRegionView::key_release (GdkEventKey* ev)
846 {
847         if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
848                 _mouse_state = None;
849                 return true;
850         }
851         return false;
852 }
853
854 void
855 MidiRegionView::channel_edit ()
856 {
857         if (_selection.empty()) {
858                 return;
859         }
860
861         /* pick a note somewhat at random (since Selection is a set<>) to
862          * provide the "current" channel for the dialog.
863          */
864
865         uint8_t current_channel = (*_selection.begin())->note()->channel ();
866         MidiChannelDialog channel_dialog (current_channel);
867         int ret = channel_dialog.run ();
868
869         switch (ret) {
870         case Gtk::RESPONSE_OK:
871                 break;
872         default:
873                 return;
874         }
875
876         uint8_t new_channel = channel_dialog.active_channel ();
877
878         start_note_diff_command (_("channel edit"));
879
880         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
881                 Selection::iterator next = i;
882                 ++next;
883                 change_note_channel (*i, new_channel);
884                 i = next;
885         }
886
887         apply_diff ();
888 }
889
890 void
891 MidiRegionView::velocity_edit ()
892 {
893         if (_selection.empty()) {
894                 return;
895         }
896
897         /* pick a note somewhat at random (since Selection is a set<>) to
898          * provide the "current" velocity for the dialog.
899          */
900
901         uint8_t current_velocity = (*_selection.begin())->note()->velocity ();
902         MidiVelocityDialog velocity_dialog (current_velocity);
903         int ret = velocity_dialog.run ();
904
905         switch (ret) {
906         case Gtk::RESPONSE_OK:
907                 break;
908         default:
909                 return;
910         }
911
912         uint8_t new_velocity = velocity_dialog.velocity ();
913
914         start_note_diff_command (_("velocity edit"));
915
916         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
917                 Selection::iterator next = i;
918                 ++next;
919                 change_note_velocity (*i, new_velocity, false);
920                 i = next;
921         }
922
923         apply_diff ();
924 }
925
926 void
927 MidiRegionView::show_list_editor ()
928 {
929         if (!_list_editor) {
930                 _list_editor = new MidiListEditor (trackview.session(), midi_region(), midi_view()->midi_track());
931         }
932         _list_editor->present ();
933 }
934
935 /** Add a note to the model, and the view, at a canvas (click) coordinate.
936  * \param t time in samples relative to the position of the region
937  * \param y vertical position in pixels
938  * \param length duration of the note in beats
939  * \param snap_t true to snap t to the grid, otherwise false.
940  */
941 void
942 MidiRegionView::create_note_at (samplepos_t t, double y, Temporal::Beats length, uint32_t state, bool shift_snap)
943 {
944         if (length < 2 * DBL_EPSILON) {
945                 return;
946         }
947
948         MidiTimeAxisView* const mtv  = dynamic_cast<MidiTimeAxisView*>(&trackview);
949         MidiStreamView* const   view = mtv->midi_view();
950         boost::shared_ptr<MidiRegion> mr  = boost::dynamic_pointer_cast<MidiRegion> (_region);
951
952         if (!mr) {
953                 return;
954         }
955
956         // Start of note in samples relative to region start
957         const int32_t divisions = trackview.editor().get_grid_music_divisions (state);
958         Temporal::Beats beat_time = snap_sample_to_grid_underneath (t, divisions, shift_snap);
959
960         const double  note     = view->y_to_note(y);
961         const uint8_t chan     = mtv->get_channel_for_add();
962         const uint8_t velocity = get_velocity_for_add(beat_time);
963
964         const boost::shared_ptr<NoteType> new_note(
965                 new NoteType (chan, beat_time, length, (uint8_t)note, velocity));
966
967         if (_model->contains (new_note)) {
968                 return;
969         }
970
971         view->update_note_range(new_note->note());
972
973         start_note_diff_command(_("add note"));
974
975         note_diff_add_note (new_note, true, false);
976
977         apply_diff();
978
979         play_midi_note (new_note);
980 }
981
982 void
983 MidiRegionView::clear_events ()
984 {
985         // clear selection without signaling
986         clear_selection_internal ();
987
988         MidiGhostRegion* gr;
989         for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
990                 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
991                         gr->clear_events();
992                 }
993         }
994
995
996         _note_group->clear (true);
997         _events.clear();
998         _patch_changes.clear();
999         _sys_exes.clear();
1000         _optimization_iterator = _events.end();
1001 }
1002
1003 void
1004 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
1005 {
1006         _model = model;
1007
1008         content_connection.disconnect ();
1009         _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
1010         /* Don't signal as nobody else needs to know until selection has been altered. */
1011         clear_events();
1012
1013         if (_enable_display) {
1014                 redisplay_model();
1015         }
1016 }
1017
1018 void
1019 MidiRegionView::start_note_diff_command (string name)
1020 {
1021         if (!_note_diff_command) {
1022                 trackview.editor().begin_reversible_command (name);
1023                 _note_diff_command = _model->new_note_diff_command (name);
1024         }
1025 }
1026
1027 void
1028 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
1029 {
1030         if (_note_diff_command) {
1031                 _note_diff_command->add (note);
1032         }
1033         if (selected) {
1034                 _marked_for_selection.insert(note);
1035         }
1036         if (show_velocity) {
1037                 _marked_for_velocity.insert(note);
1038         }
1039 }
1040
1041 void
1042 MidiRegionView::note_diff_remove_note (NoteBase* ev)
1043 {
1044         if (_note_diff_command && ev->note()) {
1045                 _note_diff_command->remove(ev->note());
1046         }
1047 }
1048
1049 void
1050 MidiRegionView::note_diff_add_change (NoteBase* ev,
1051                                       MidiModel::NoteDiffCommand::Property property,
1052                                       uint8_t val)
1053 {
1054         if (_note_diff_command) {
1055                 _note_diff_command->change (ev->note(), property, val);
1056         }
1057 }
1058
1059 void
1060 MidiRegionView::note_diff_add_change (NoteBase* ev,
1061                                       MidiModel::NoteDiffCommand::Property property,
1062                                       Temporal::Beats val)
1063 {
1064         if (_note_diff_command) {
1065                 _note_diff_command->change (ev->note(), property, val);
1066         }
1067 }
1068
1069 void
1070 MidiRegionView::apply_diff (bool as_subcommand, bool was_copy)
1071 {
1072         bool commit = false;
1073
1074         if (!_note_diff_command) {
1075                 return;
1076         }
1077
1078         bool add_or_remove = _note_diff_command->adds_or_removes();
1079
1080         if (!was_copy && add_or_remove) {
1081                 // Mark all selected notes for selection when model reloads
1082                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1083                         _marked_for_selection.insert((*i)->note());
1084                 }
1085         }
1086
1087         if (as_subcommand) {
1088                 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
1089         } else {
1090                 _model->apply_command (*trackview.session(), _note_diff_command);
1091                 commit = true;
1092         }
1093
1094         _note_diff_command = 0;
1095
1096         if (add_or_remove) {
1097                 _marked_for_selection.clear();
1098         }
1099
1100         _marked_for_velocity.clear();
1101
1102         if (commit) {
1103                 trackview.editor().commit_reversible_command ();
1104         }
1105 }
1106
1107 void
1108 MidiRegionView::abort_command()
1109 {
1110         delete _note_diff_command;
1111         _note_diff_command = 0;
1112         trackview.editor().abort_reversible_command();
1113         clear_editor_note_selection();
1114 }
1115
1116 NoteBase*
1117 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
1118 {
1119
1120         if (_optimization_iterator != _events.end()) {
1121                 ++_optimization_iterator;
1122         }
1123
1124         if (_optimization_iterator != _events.end() && _optimization_iterator->first == note) {
1125                 return _optimization_iterator->second;
1126         }
1127
1128         _optimization_iterator = _events.find (note);
1129         if (_optimization_iterator != _events.end()) {
1130                 return _optimization_iterator->second;
1131         }
1132
1133         return 0;
1134 }
1135
1136 /** This version finds any canvas note matching the supplied note. */
1137 NoteBase*
1138 MidiRegionView::find_canvas_note (Evoral::event_id_t id)
1139 {
1140         Events::iterator it;
1141
1142         for (it = _events.begin(); it != _events.end(); ++it) {
1143                 if (it->first->id() == id) {
1144                         return it->second;
1145                 }
1146         }
1147
1148         return 0;
1149 }
1150
1151 boost::shared_ptr<PatchChange>
1152 MidiRegionView::find_canvas_patch_change (MidiModel::PatchChangePtr p)
1153 {
1154         PatchChanges::const_iterator f = _patch_changes.find (p);
1155
1156         if (f != _patch_changes.end()) {
1157                 return f->second;
1158         }
1159
1160         return boost::shared_ptr<PatchChange>();
1161 }
1162
1163 boost::shared_ptr<SysEx>
1164 MidiRegionView::find_canvas_sys_ex (MidiModel::SysExPtr s)
1165 {
1166         SysExes::const_iterator f = _sys_exes.find (s);
1167
1168         if (f != _sys_exes.end()) {
1169                 return f->second;
1170         }
1171
1172         return boost::shared_ptr<SysEx>();
1173 }
1174
1175 void
1176 MidiRegionView::get_events (Events& e, Evoral::Sequence<Temporal::Beats>::NoteOperator op, uint8_t val, int chan_mask)
1177 {
1178         MidiModel::Notes notes;
1179         _model->get_notes (notes, op, val, chan_mask);
1180
1181         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1182                 NoteBase* cne = find_canvas_note (*n);
1183                 if (cne) {
1184                         e.insert (make_pair (*n, cne));
1185                 }
1186         }
1187 }
1188
1189 void
1190 MidiRegionView::redisplay_model()
1191 {
1192         if (_active_notes) {
1193                 // Currently recording
1194                 const samplecnt_t zoom = trackview.editor().get_current_zoom();
1195                 if (zoom != _last_display_zoom) {
1196                         /* Update resolved canvas notes to reflect changes in zoom without
1197                            touching model.  Leave active notes (with length max) alone since
1198                            they are being extended. */
1199                         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1200                                 if (i->second->note()->end_time() != std::numeric_limits<Temporal::Beats>::max()) {
1201                                         update_note(i->second);
1202                                 }
1203                         }
1204                         _last_display_zoom = zoom;
1205                 }
1206                 return;
1207         }
1208
1209         if (!_model) {
1210                 return;
1211         }
1212
1213         for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
1214                 _optimization_iterator->second->invalidate();
1215         }
1216
1217         bool empty_when_starting = _events.empty();
1218         _optimization_iterator = _events.begin();
1219         MidiModel::Notes missing_notes;
1220         Note* sus = NULL;
1221         Hit*  hit = NULL;
1222
1223         MidiModel::ReadLock lock(_model->read_lock());
1224         MidiModel::Notes& notes (_model->notes());
1225
1226         NoteBase* cne;
1227         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1228
1229                 boost::shared_ptr<NoteType> note (*n);
1230                 bool visible;
1231
1232                 if (note_in_region_range (note, visible)) {
1233                         if (!empty_when_starting && (cne = find_canvas_note (note)) != 0) {
1234                                 cne->validate ();
1235                                 if (visible) {
1236                                         cne->show ();
1237                                 } else {
1238                                         cne->hide ();
1239                                 }
1240                         } else {
1241                                 missing_notes.insert (note);
1242                         }
1243                 }
1244         }
1245
1246         if (!empty_when_starting) {
1247                 MidiModel::Notes::iterator f;
1248                 for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1249
1250                         NoteBase* cne = i->second;
1251
1252                         /* remove note items that are no longer valid */
1253                         if (!cne->valid()) {
1254
1255                                 for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1256                                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1257                                         if (gr) {
1258                                                 gr->remove_note (cne);
1259                                         }
1260                                 }
1261
1262                                 delete cne;
1263                                 i = _events.erase (i);
1264
1265                         } else {
1266                                 bool visible = cne->item()->visible();
1267
1268                                 if ((sus = dynamic_cast<Note*>(cne))) {
1269
1270                                         if (visible) {
1271                                                 update_sustained (sus);
1272                                         }
1273
1274                                 } else if ((hit = dynamic_cast<Hit*>(cne))) {
1275
1276                                         if (visible) {
1277                                                 update_hit (hit);
1278                                         }
1279
1280                                 }
1281                                 ++i;
1282                         }
1283                 }
1284         }
1285
1286         for (MidiModel::Notes::iterator n = missing_notes.begin(); n != missing_notes.end(); ++n) {
1287                 boost::shared_ptr<NoteType> note (*n);
1288                 NoteBase* cne;
1289                 bool visible;
1290
1291                 if (note_in_region_range (note, visible)) {
1292                         if (visible) {
1293                                 cne = add_note (note, true);
1294                         } else {
1295                                 cne = add_note (note, false);
1296                         }
1297                 } else {
1298                         cne = add_note (note, false);
1299                 }
1300
1301                 for (set<Evoral::event_id_t>::iterator it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
1302                         if ((*it) == note->id()) {
1303                                 add_to_selection (cne);
1304                         }
1305                 }
1306         }
1307
1308         for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1309                 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1310                 if (gr && !gr->trackview.hidden()) {
1311                         gr->redisplay_model ();
1312                 }
1313         }
1314
1315         display_sysexes();
1316         display_patch_changes ();
1317
1318         _marked_for_selection.clear ();
1319         _marked_for_velocity.clear ();
1320         _pending_note_selection.clear ();
1321
1322 }
1323
1324 void
1325 MidiRegionView::display_patch_changes ()
1326 {
1327         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1328         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1329
1330         for (uint8_t i = 0; i < 16; ++i) {
1331                 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1332         }
1333 }
1334
1335 /** @param active_channel true to display patch changes fully, false to display
1336  * them `greyed-out' (as on an inactive channel)
1337  */
1338 void
1339 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1340 {
1341         for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1342                 boost::shared_ptr<PatchChange> p;
1343
1344                 if ((*i)->channel() != channel) {
1345                         continue;
1346                 }
1347
1348                 if ((p = find_canvas_patch_change (*i)) != 0) {
1349
1350                         const samplecnt_t region_samples = source_beats_to_region_samples ((*i)->time());
1351
1352                         if (region_samples < 0 || region_samples >= _region->length()) {
1353                                 p->hide();
1354                         } else {
1355                                 const double x = trackview.editor().sample_to_pixel (region_samples);
1356                                 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1357                                 p->canvas_item()->set_position (ArdourCanvas::Duple (x, 1.0));
1358                                 p->set_text (patch_name);
1359
1360                                 p->show();
1361                         }
1362
1363                 } else {
1364                         const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1365                         add_canvas_patch_change (*i, patch_name, active_channel);
1366                 }
1367         }
1368 }
1369
1370 void
1371 MidiRegionView::display_sysexes()
1372 {
1373         bool have_periodic_system_messages = false;
1374         bool display_periodic_messages = true;
1375
1376         if (!UIConfiguration::instance().get_never_display_periodic_midi()) {
1377
1378                 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1379                         if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
1380                                 have_periodic_system_messages = true;
1381                                 break;
1382                         }
1383                 }
1384
1385                 if (have_periodic_system_messages) {
1386                         double zoom = trackview.editor().get_current_zoom (); // samples per pixel
1387
1388                         /* get an approximate value for the number of samples per video frame */
1389
1390                         double video_frame = trackview.session()->sample_rate() * (1.0/30);
1391
1392                         /* if we are zoomed out beyond than the cutoff (i.e. more
1393                          * samples per pixel than samples per 4 video frames), don't
1394                          * show periodic sysex messages.
1395                          */
1396
1397                         if (zoom > (video_frame*4)) {
1398                                 display_periodic_messages = false;
1399                         }
1400                 }
1401         } else {
1402                 display_periodic_messages = false;
1403         }
1404
1405         const boost::shared_ptr<MidiRegion> mregion (midi_region());
1406
1407         for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1408                 MidiModel::SysExPtr sysex_ptr = *i;
1409                 Temporal::Beats time = sysex_ptr->time();
1410
1411                 if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
1412                         if (!display_periodic_messages) {
1413                                 continue;
1414                         }
1415                 }
1416
1417                 ostringstream str;
1418                 str << hex;
1419                 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1420                         str << int((*i)->buffer()[b]);
1421                         if (b != (*i)->size() -1) {
1422                                 str << " ";
1423                         }
1424                 }
1425                 string text = str.str();
1426
1427                 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_samples(time));
1428
1429                 double height = midi_stream_view()->contents_height();
1430
1431                 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1432                 // SysEx canvas object!!!
1433                 boost::shared_ptr<SysEx> sysex = find_canvas_sys_ex (sysex_ptr);
1434
1435                 if (!sysex) {
1436                         sysex = boost::shared_ptr<SysEx>(
1437                                 new SysEx (*this, _note_group, text, height, x, 1.0, sysex_ptr));
1438                         _sys_exes.insert (make_pair (sysex_ptr, sysex));
1439                 } else {
1440                         sysex->set_height (height);
1441                         sysex->item().set_position (ArdourCanvas::Duple (x, 1.0));
1442                 }
1443
1444                 // Show unless message is beyond the region bounds
1445                 if (time - mregion->start_beats() >= mregion->length_beats() || time < mregion->start_beats()) {
1446                         sysex->hide();
1447                 } else {
1448                         sysex->show();
1449                 }
1450         }
1451 }
1452
1453 MidiRegionView::~MidiRegionView ()
1454 {
1455         in_destructor = true;
1456
1457         hide_verbose_cursor ();
1458
1459         delete _list_editor;
1460
1461         RegionViewGoingAway (this); /* EMIT_SIGNAL */
1462
1463         if (_active_notes) {
1464                 end_write();
1465         }
1466         _entered_note = 0;
1467         clear_events ();
1468
1469         delete _note_group;
1470         delete _note_diff_command;
1471         delete _step_edit_cursor;
1472 }
1473
1474 void
1475 MidiRegionView::region_resized (const PropertyChange& what_changed)
1476 {
1477         RegionView::region_resized(what_changed); // calls RegionView::set_duration()
1478
1479         if (what_changed.contains (ARDOUR::Properties::position)) {
1480                 _region_relative_time_converter.set_origin_b(_region->position());
1481                 _region_relative_time_converter_double.set_origin_b(_region->position());
1482                 /* reset_width dependent_items() redisplays model */
1483
1484         }
1485
1486         if (what_changed.contains (ARDOUR::Properties::start) ||
1487             what_changed.contains (ARDOUR::Properties::position)) {
1488                 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1489         }
1490         /* catch end and start trim so we can update the view*/
1491         if (!what_changed.contains (ARDOUR::Properties::start) &&
1492             what_changed.contains (ARDOUR::Properties::length)) {
1493                 enable_display (true);
1494         } else if (what_changed.contains (ARDOUR::Properties::start) &&
1495             what_changed.contains (ARDOUR::Properties::length)) {
1496                 enable_display (true);
1497         }
1498 }
1499
1500 void
1501 MidiRegionView::reset_width_dependent_items (double pixel_width)
1502 {
1503         RegionView::reset_width_dependent_items(pixel_width);
1504
1505         if (_enable_display) {
1506                 redisplay_model();
1507         }
1508
1509         bool hide_all = false;
1510         PatchChanges::iterator x = _patch_changes.begin();
1511         if (x != _patch_changes.end()) {
1512                 hide_all = x->second->width() >= _pixel_width;
1513         }
1514
1515         if (hide_all) {
1516                 for (; x != _patch_changes.end(); ++x) {
1517                         x->second->hide();
1518                 }
1519         }
1520
1521         move_step_edit_cursor (_step_edit_cursor_position);
1522         set_step_edit_cursor_width (_step_edit_cursor_width);
1523 }
1524
1525 void
1526 MidiRegionView::set_height (double height)
1527 {
1528         double old_height = _height;
1529         RegionView::set_height(height);
1530
1531         apply_note_range (midi_stream_view()->lowest_note(),
1532                           midi_stream_view()->highest_note(),
1533                           height != old_height);
1534
1535         if (name_text) {
1536                 name_text->raise_to_top();
1537         }
1538
1539         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1540                 (*x).second->set_height (midi_stream_view()->contents_height());
1541         }
1542
1543         if (_step_edit_cursor) {
1544                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1545         }
1546 }
1547
1548
1549 /** Apply the current note range from the stream view
1550  * by repositioning/hiding notes as necessary
1551  */
1552 void
1553 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1554 {
1555         if (!_enable_display) {
1556                 return;
1557         }
1558
1559         if (!force && _current_range_min == min && _current_range_max == max) {
1560                 return;
1561         }
1562
1563         _current_range_min = min;
1564         _current_range_max = max;
1565
1566         redisplay_model ();
1567 }
1568
1569 GhostRegion*
1570 MidiRegionView::add_ghost (TimeAxisView& tv)
1571 {
1572         double unit_position = _region->position () / samples_per_pixel;
1573         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1574         MidiGhostRegion* ghost;
1575
1576         if (mtv && mtv->midi_view()) {
1577                 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1578                    to allow having midi notes on top of note lines and waveforms.
1579                 */
1580                 ghost = new MidiGhostRegion (*this, *mtv->midi_view(), trackview, unit_position);
1581         } else {
1582                 ghost = new MidiGhostRegion (*this, tv, trackview, unit_position);
1583         }
1584
1585         ghost->set_colors ();
1586         ghost->set_height ();
1587         ghost->set_duration (_region->length() / samples_per_pixel);
1588
1589         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1590                 ghost->add_note(i->second);
1591         }
1592
1593         ghosts.push_back (ghost);
1594         enable_display (true);
1595         return ghost;
1596 }
1597
1598
1599 /** Begin tracking note state for successive calls to add_event
1600  */
1601 void
1602 MidiRegionView::begin_write()
1603 {
1604         if (_active_notes) {
1605                 delete[] _active_notes;
1606         }
1607         _active_notes = new Note*[128];
1608         for (unsigned i = 0; i < 128; ++i) {
1609                 _active_notes[i] = 0;
1610         }
1611 }
1612
1613
1614 /** Destroy note state for add_event
1615  */
1616 void
1617 MidiRegionView::end_write()
1618 {
1619         delete[] _active_notes;
1620         _active_notes = 0;
1621         _marked_for_selection.clear();
1622         _marked_for_velocity.clear();
1623 }
1624
1625
1626 /** Resolve an active MIDI note (while recording).
1627  */
1628 void
1629 MidiRegionView::resolve_note (uint8_t note, Temporal::Beats end_time)
1630 {
1631         if (midi_view()->note_mode() != Sustained) {
1632                 return;
1633         }
1634
1635         if (_active_notes && _active_notes[note]) {
1636                 /* Set note length so update_note() works.  Note this is a local note
1637                    for recording, not from a model, so we can safely mess with it. */
1638                 _active_notes[note]->note()->set_length (end_time - _active_notes[note]->note()->time());
1639
1640                 /* End time is relative to the region being recorded. */
1641                 const samplepos_t end_time_samples = region_beats_to_region_samples (end_time);
1642
1643                 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_samples));
1644                 _active_notes[note]->set_outline_all ();
1645                 _active_notes[note] = 0;
1646         }
1647 }
1648
1649
1650 /** Extend active notes to rightmost edge of region (if length is changed)
1651  */
1652 void
1653 MidiRegionView::extend_active_notes()
1654 {
1655         if (!_active_notes) {
1656                 return;
1657         }
1658
1659         for (unsigned i = 0; i < 128; ++i) {
1660                 if (_active_notes[i]) {
1661                         _active_notes[i]->set_x1 (trackview.editor().sample_to_pixel(_region->length()));
1662                 }
1663         }
1664 }
1665
1666 void
1667 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1668 {
1669         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1670                 return;
1671         }
1672
1673         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1674
1675         if (!route_ui || !route_ui->midi_track()) {
1676                 return;
1677         }
1678
1679         NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1680         np->add (note);
1681         np->play ();
1682
1683         /* NotePlayer deletes itself */
1684 }
1685
1686 void
1687 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1688 {
1689         const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1690         start_playing_midi_chord(notes);
1691 }
1692
1693 void
1694 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1695 {
1696         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1697                 return;
1698         }
1699
1700         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1701
1702         if (!route_ui || !route_ui->midi_track()) {
1703                 return;
1704         }
1705
1706         NotePlayer* player = new NotePlayer (route_ui->midi_track());
1707
1708         for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1709                 player->add (*n);
1710         }
1711
1712         player->play ();
1713 }
1714
1715
1716 bool
1717 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1718 {
1719         const boost::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region();
1720
1721         /* must compare double explicitly as Beats::operator< rounds to ppqn */
1722         const bool outside = (note->time().to_double() < midi_reg->start_beats() ||
1723                               note->time().to_double() >= midi_reg->start_beats() + midi_reg->length_beats());
1724
1725         visible = (note->note() >= _current_range_min) &&
1726                 (note->note() <= _current_range_max);
1727
1728         return !outside;
1729 }
1730
1731 void
1732 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1733 {
1734         Note* sus = NULL;
1735         Hit*  hit = NULL;
1736         if ((sus = dynamic_cast<Note*>(note))) {
1737                 update_sustained(sus, update_ghost_regions);
1738         } else if ((hit = dynamic_cast<Hit*>(note))) {
1739                 update_hit(hit, update_ghost_regions);
1740         }
1741 }
1742
1743 /** Update a canvas note's size from its model note.
1744  *  @param ev Canvas note to update.
1745  *  @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1746  */
1747 void
1748 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1749 {
1750         TempoMap& map (trackview.session()->tempo_map());
1751         const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
1752         boost::shared_ptr<NoteType> note = ev->note();
1753
1754         const double session_source_start = _region->quarter_note() - mr->start_beats();
1755         const samplepos_t note_start_samples = map.sample_at_quarter_note (note->time().to_double() + session_source_start) - _region->position();
1756
1757         const double x0 = max (0.,trackview.editor().sample_to_pixel (note_start_samples));
1758         double x1;
1759         const double y0 = 1 + floor(note_to_y(note->note()));
1760         double y1;
1761
1762         if (note->length() == 0) {
1763
1764                 /* special case actual zero-length notes */
1765
1766                 x1 = x0 + 1.;
1767
1768         } else if (note->end_time() != std::numeric_limits<Temporal::Beats>::max()) {
1769
1770                 /* normal note */
1771
1772                 double note_end_time = note->end_time().to_double();
1773
1774                 if (note->end_time() > mr->start_beats() + mr->length_beats()) {
1775                         note_end_time = mr->start_beats() + mr->length_beats();
1776                 }
1777
1778                 const samplepos_t note_end_samples = map.sample_at_quarter_note (session_source_start + note_end_time) - _region->position();
1779
1780                 x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_samples)) - 1;
1781
1782         } else {
1783
1784                 /* nascent note currently being recorded, noteOff has not yet arrived */
1785
1786                 x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
1787         }
1788
1789         y1 = y0 + std::max(1., floor(note_height()) - 1);
1790
1791         ev->set (ArdourCanvas::Rect (x0, y0, x1, y1));
1792         ev->set_velocity (note->velocity()/127.0);
1793
1794         if (note->end_time() == std::numeric_limits<Temporal::Beats>::max())  {
1795                 if (_active_notes && note->note() < 128) {
1796                         Note* const old_rect = _active_notes[note->note()];
1797                         if (old_rect) {
1798                                 /* There is an active note on this key, so we have a stuck
1799                                    note.  Finish the old rectangle here. */
1800                                 old_rect->set_x1 (x1);
1801                                 old_rect->set_outline_all ();
1802                         }
1803                         _active_notes[note->note()] = ev;
1804                 }
1805                 /* outline all but right edge */
1806                 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1807                                               ArdourCanvas::Rectangle::TOP|
1808                                               ArdourCanvas::Rectangle::LEFT|
1809                                               ArdourCanvas::Rectangle::BOTTOM));
1810         } else {
1811                 /* outline all edges */
1812                 ev->set_outline_all ();
1813         }
1814
1815         // Update color in case velocity has changed
1816         const uint32_t base_col = ev->base_color();
1817         ev->set_fill_color(base_col);
1818         ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
1819
1820 }
1821
1822 void
1823 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1824 {
1825         boost::shared_ptr<NoteType> note = ev->note();
1826
1827         const double note_time_qn = note->time().to_double() + (_region->quarter_note() - midi_region()->start_beats());
1828         const samplepos_t note_start_samples = trackview.session()->tempo_map().sample_at_quarter_note (note_time_qn) - _region->position();
1829
1830         const double x = trackview.editor().sample_to_pixel(note_start_samples);
1831         const double diamond_size = std::max(1., floor(note_height()) - 2.);
1832         const double y = 1.5 + floor(note_to_y(note->note())) + diamond_size * .5;
1833
1834         // see DnD note in MidiRegionView::apply_note_range() above
1835         if (y <= 0 || y >= _height) {
1836                 ev->hide();
1837         } else {
1838                 ev->show();
1839         }
1840
1841         ev->set_position (ArdourCanvas::Duple (x, y));
1842         ev->set_height (diamond_size);
1843
1844         // Update color in case velocity has changed
1845         const uint32_t base_col = ev->base_color();
1846         ev->set_fill_color(base_col);
1847         ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
1848
1849 }
1850
1851 /** Add a MIDI note to the view (with length).
1852  *
1853  * If in sustained mode, notes with an end at numeric_limits<Beats>::max() will be
1854  * considered active notes, and resolve_note should be called when the
1855  * corresponding note off event arrives, to properly display the note.
1856  */
1857 NoteBase*
1858 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1859 {
1860         NoteBase* event = 0;
1861
1862         if (midi_view()->note_mode() == Sustained) {
1863
1864                 Note* ev_rect = new Note (*this, _note_group, note); // XXX may leak
1865
1866                 update_sustained (ev_rect);
1867
1868                 event = ev_rect;
1869
1870         } else if (midi_view()->note_mode() == Percussive) {
1871
1872                 const double diamond_size = std::max(1., floor(note_height()) - 2.);
1873
1874                 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); // XXX may leak
1875
1876                 update_hit (ev_diamond);
1877
1878                 event = ev_diamond;
1879
1880         } else {
1881                 event = 0;
1882         }
1883
1884         if (event) {
1885                 MidiGhostRegion* gr;
1886
1887                 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1888                         if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1889                                 gr->add_note(event);
1890                         }
1891                 }
1892
1893                 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1894                         note_selected(event, true);
1895                 }
1896
1897                 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1898                         event->show_velocity();
1899                 }
1900
1901                 event->on_channel_selection_change (get_selected_channels());
1902                 _events.insert (make_pair (event->note(), event));
1903
1904                 if (visible) {
1905                         event->show();
1906                 } else {
1907                         event->hide ();
1908                 }
1909         }
1910
1911         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1912         MidiStreamView* const view = mtv->midi_view();
1913
1914         view->update_note_range (note->note());
1915         return event;
1916 }
1917
1918 void
1919 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1920                                Temporal::Beats pos, Temporal::Beats len)
1921 {
1922         boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1923
1924         /* potentially extend region to hold new note */
1925
1926         samplepos_t end_sample = source_beats_to_absolute_samples (new_note->end_time());
1927         samplepos_t region_end = _region->last_sample();
1928
1929         if (end_sample > region_end) {
1930                 /* XX sets length in beats from audio space. make musical */
1931                 _region->set_length (end_sample - _region->position(), 0);
1932         }
1933
1934         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1935         MidiStreamView* const view = mtv->midi_view();
1936
1937         view->update_note_range(new_note->note());
1938
1939         _marked_for_selection.clear ();
1940
1941         start_note_diff_command (_("step add"));
1942
1943         clear_editor_note_selection ();
1944         note_diff_add_note (new_note, true, false);
1945
1946         apply_diff();
1947
1948         // last_step_edit_note = new_note;
1949 }
1950
1951 void
1952 MidiRegionView::step_sustain (Temporal::Beats beats)
1953 {
1954         change_note_lengths (false, false, beats, false, true);
1955 }
1956
1957 /** Add a new patch change flag to the canvas.
1958  * @param patch the patch change to add
1959  * @param the text to display in the flag
1960  * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1961  */
1962 void
1963 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1964 {
1965         samplecnt_t region_samples = source_beats_to_region_samples (patch->time());
1966         const double x = trackview.editor().sample_to_pixel (region_samples);
1967
1968         double const height = midi_stream_view()->contents_height();
1969
1970         // CAIROCANVAS: active_channel info removed from PatcChange constructor
1971         // so we need to do something more sophisticated to keep its color
1972         // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1973         // up to date.
1974         boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1975                 new PatchChange(*this, group,
1976                                 displaytext,
1977                                 height,
1978                                 x, 1.0,
1979                                 instrument_info(),
1980                                 patch,
1981                                 _patch_change_outline,
1982                                 _patch_change_fill)
1983                 );
1984
1985         if (patch_change->item().width() < _pixel_width) {
1986                 // Show unless patch change is beyond the region bounds
1987                 if (region_samples < 0 || region_samples >= _region->length()) {
1988                         patch_change->hide();
1989                 } else {
1990                         patch_change->show();
1991                 }
1992         } else {
1993                 patch_change->hide ();
1994         }
1995
1996         _patch_changes.insert (make_pair (patch, patch_change));
1997 }
1998
1999 void
2000 MidiRegionView::remove_canvas_patch_change (PatchChange* pc)
2001 {
2002         /* remove the canvas item */
2003         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
2004                 if (x->second->patch() == pc->patch()) {
2005                         _patch_changes.erase (x);
2006                         break;
2007                 }
2008         }
2009 }
2010
2011 MIDI::Name::PatchPrimaryKey
2012 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
2013 {
2014         return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
2015 }
2016
2017 /// Return true iff @p pc applies to the given time on the given channel.
2018 static bool
2019 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Temporal::Beats time, uint8_t channel)
2020 {
2021         return pc->time() <= time && pc->channel() == channel;
2022 }
2023
2024 void
2025 MidiRegionView::get_patch_key_at (Temporal::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
2026 {
2027         // The earliest event not before time
2028         MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
2029
2030         // Go backwards until we find the latest PC for this channel, or the start
2031         while (i != _model->patch_changes().begin() &&
2032                (i == _model->patch_changes().end() ||
2033                 !patch_applies(*i, time, channel))) {
2034                 --i;
2035         }
2036
2037         if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
2038                 key.set_bank((*i)->bank());
2039                 key.set_program((*i)->program ());
2040         } else {
2041                 key.set_bank(0);
2042                 key.set_program(0);
2043         }
2044 }
2045
2046 void
2047 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
2048 {
2049         string name = _("alter patch change");
2050         trackview.editor().begin_reversible_command (name);
2051
2052         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2053
2054         if (pc.patch()->program() != new_patch.program()) {
2055                 c->change_program (pc.patch (), new_patch.program());
2056         }
2057
2058         int const new_bank = new_patch.bank();
2059         if (pc.patch()->bank() != new_bank) {
2060                 c->change_bank (pc.patch (), new_bank);
2061         }
2062
2063         _model->apply_command (*trackview.session(), c);
2064         trackview.editor().commit_reversible_command ();
2065
2066         remove_canvas_patch_change (&pc);
2067         display_patch_changes ();
2068 }
2069
2070 void
2071 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Temporal::Beats> & new_change)
2072 {
2073         string name = _("alter patch change");
2074         trackview.editor().begin_reversible_command (name);
2075         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2076
2077         if (old_change->time() != new_change.time()) {
2078                 c->change_time (old_change, new_change.time());
2079         }
2080
2081         if (old_change->channel() != new_change.channel()) {
2082                 c->change_channel (old_change, new_change.channel());
2083         }
2084
2085         if (old_change->program() != new_change.program()) {
2086                 c->change_program (old_change, new_change.program());
2087         }
2088
2089         if (old_change->bank() != new_change.bank()) {
2090                 c->change_bank (old_change, new_change.bank());
2091         }
2092
2093         _model->apply_command (*trackview.session(), c);
2094         trackview.editor().commit_reversible_command ();
2095
2096         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
2097                 if (x->second->patch() == old_change) {
2098                         _patch_changes.erase (x);
2099                         break;
2100                 }
2101         }
2102
2103         display_patch_changes ();
2104 }
2105
2106 /** Add a patch change to the region.
2107  *  @param t Time in samples relative to region position
2108  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2109  *  MidiTimeAxisView::get_channel_for_add())
2110  */
2111 void
2112 MidiRegionView::add_patch_change (samplecnt_t t, Evoral::PatchChange<Temporal::Beats> const & patch)
2113 {
2114         string name = _("add patch change");
2115
2116         trackview.editor().begin_reversible_command (name);
2117         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2118         c->add (MidiModel::PatchChangePtr (
2119                         new Evoral::PatchChange<Temporal::Beats> (
2120                                 absolute_samples_to_source_beats (_region->position() + t),
2121                                 patch.channel(), patch.program(), patch.bank()
2122                                 )
2123                         )
2124                 );
2125
2126         _model->apply_command (*trackview.session(), c);
2127         trackview.editor().commit_reversible_command ();
2128
2129         display_patch_changes ();
2130 }
2131
2132 void
2133 MidiRegionView::move_patch_change (PatchChange& pc, Temporal::Beats t)
2134 {
2135         trackview.editor().begin_reversible_command (_("move patch change"));
2136         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2137         c->change_time (pc.patch (), t);
2138         _model->apply_command (*trackview.session(), c);
2139         trackview.editor().commit_reversible_command ();
2140
2141         display_patch_changes ();
2142 }
2143
2144 void
2145 MidiRegionView::delete_patch_change (PatchChange* pc)
2146 {
2147         trackview.editor().begin_reversible_command (_("delete patch change"));
2148
2149         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2150         c->remove (pc->patch ());
2151         _model->apply_command (*trackview.session(), c);
2152         trackview.editor().commit_reversible_command ();
2153
2154         remove_canvas_patch_change (pc);
2155         display_patch_changes ();
2156 }
2157
2158 void
2159 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2160 {
2161         MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2162         if (bank) {
2163                 key.set_bank(key.bank() + delta);
2164         } else {
2165                 key.set_program(key.program() + delta);
2166         }
2167         change_patch_change(patch, key);
2168 }
2169
2170 void
2171 MidiRegionView::note_deleted (NoteBase* cne)
2172 {
2173         if (_entered_note && cne == _entered_note) {
2174                 _entered_note = 0;
2175         }
2176
2177         if (_selection.empty()) {
2178                 return;
2179         }
2180
2181         _selection.erase (cne);
2182 }
2183
2184 void
2185 MidiRegionView::delete_selection()
2186 {
2187         if (_selection.empty()) {
2188                 return;
2189         }
2190
2191         if (trackview.editor().drags()->active()) {
2192                 return;
2193         }
2194
2195         start_note_diff_command (_("delete selection"));
2196
2197         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2198                 if ((*i)->selected()) {
2199                         _note_diff_command->remove((*i)->note());
2200                 }
2201         }
2202
2203         _selection.clear();
2204
2205         apply_diff ();
2206
2207         hide_verbose_cursor ();
2208 }
2209
2210 void
2211 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2212 {
2213         start_note_diff_command (_("delete note"));
2214         _note_diff_command->remove (n);
2215         apply_diff ();
2216
2217         hide_verbose_cursor ();
2218 }
2219
2220 void
2221 MidiRegionView::clear_editor_note_selection ()
2222 {
2223         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_editor_note_selection\n");
2224         PublicEditor& editor(trackview.editor());
2225         editor.get_selection().clear_midi_notes();
2226 }
2227
2228 void
2229 MidiRegionView::clear_selection ()
2230 {
2231         clear_selection_internal();
2232         PublicEditor& editor(trackview.editor());
2233         editor.get_selection().remove(this);
2234 }
2235
2236 void
2237 MidiRegionView::clear_selection_internal ()
2238 {
2239         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_selection_internal\n");
2240
2241         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2242                 (*i)->set_selected(false);
2243                 (*i)->hide_velocity();
2244         }
2245         _selection.clear();
2246
2247         if (_entered) {
2248                 // Clearing selection entirely, ungrab keyboard
2249                 Keyboard::magic_widget_drop_focus();
2250                 _grabbed_keyboard = false;
2251         }
2252 }
2253
2254 void
2255 MidiRegionView::unique_select(NoteBase* ev)
2256 {
2257         clear_editor_note_selection();
2258         add_to_selection(ev);
2259 }
2260
2261 void
2262 MidiRegionView::select_all_notes ()
2263 {
2264         clear_editor_note_selection ();
2265
2266         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2267                 add_to_selection (i->second);
2268         }
2269 }
2270
2271 void
2272 MidiRegionView::select_range (samplepos_t start, samplepos_t end)
2273 {
2274         clear_editor_note_selection ();
2275
2276         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2277                 samplepos_t t = source_beats_to_absolute_samples(i->first->time());
2278                 if (t >= start && t <= end) {
2279                         add_to_selection (i->second);
2280                 }
2281         }
2282 }
2283
2284 void
2285 MidiRegionView::invert_selection ()
2286 {
2287         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2288                 if (i->second->selected()) {
2289                         remove_from_selection(i->second);
2290                 } else {
2291                         add_to_selection (i->second);
2292                 }
2293         }
2294 }
2295
2296 /** Used for selection undo/redo.
2297     The requested notes most likely won't exist in the view until the next model redisplay.
2298 */
2299 void
2300 MidiRegionView::select_notes (list<Evoral::event_id_t> notes)
2301 {
2302         NoteBase* cne;
2303         list<Evoral::event_id_t>::iterator n;
2304
2305         for (n = notes.begin(); n != notes.end(); ++n) {
2306                 if ((cne = find_canvas_note(*n)) != 0) {
2307                         add_to_selection (cne);
2308                 } else {
2309                         _pending_note_selection.insert(*n);
2310                 }
2311         }
2312 }
2313
2314 void
2315 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2316 {
2317         bool have_selection = !_selection.empty();
2318         uint8_t low_note = 127;
2319         uint8_t high_note = 0;
2320         MidiModel::Notes& notes (_model->notes());
2321         _optimization_iterator = _events.begin();
2322
2323         if (extend && !have_selection) {
2324                 extend = false;
2325         }
2326
2327         /* scan existing selection to get note range */
2328
2329         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2330                 if ((*i)->note()->note() < low_note) {
2331                         low_note = (*i)->note()->note();
2332                 }
2333                 if ((*i)->note()->note() > high_note) {
2334                         high_note = (*i)->note()->note();
2335                 }
2336         }
2337
2338         if (!add) {
2339                 clear_editor_note_selection ();
2340
2341                 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2342                         /* only note previously selected is the one we are
2343                          * reselecting. treat this as cancelling the selection.
2344                          */
2345                         return;
2346                 }
2347         }
2348
2349         if (extend) {
2350                 low_note = min (low_note, notenum);
2351                 high_note = max (high_note, notenum);
2352         }
2353
2354         _no_sound_notes = true;
2355
2356         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2357
2358                 boost::shared_ptr<NoteType> note (*n);
2359                 NoteBase* cne;
2360                 bool select = false;
2361
2362                 if (((1 << note->channel()) & channel_mask) != 0) {
2363                         if (extend) {
2364                                 if ((note->note() >= low_note && note->note() <= high_note)) {
2365                                         select = true;
2366                                 }
2367                         } else if (note->note() == notenum) {
2368                                 select = true;
2369                         }
2370                 }
2371
2372                 if (select) {
2373                         if ((cne = find_canvas_note (note)) != 0) {
2374                                 // extend is false because we've taken care of it,
2375                                 // since it extends by time range, not pitch.
2376                                 note_selected (cne, add, false);
2377                         }
2378                 }
2379
2380                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2381
2382         }
2383
2384         _no_sound_notes = false;
2385 }
2386
2387 void
2388 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2389 {
2390         MidiModel::Notes& notes (_model->notes());
2391         _optimization_iterator = _events.begin();
2392
2393         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2394
2395                 boost::shared_ptr<NoteType> note (*n);
2396                 NoteBase* cne;
2397
2398                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2399                         if ((cne = find_canvas_note (note)) != 0) {
2400                                 if (cne->selected()) {
2401                                         note_deselected (cne);
2402                                 } else {
2403                                         note_selected (cne, true, false);
2404                                 }
2405                         }
2406                 }
2407         }
2408 }
2409
2410 void
2411 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2412 {
2413         if (!add) {
2414                 clear_editor_note_selection();
2415                 add_to_selection (ev);
2416         }
2417
2418         if (!extend) {
2419
2420                 if (!ev->selected()) {
2421                         add_to_selection (ev);
2422                 }
2423
2424         } else {
2425                 /* find end of latest note selected, select all between that and the start of "ev" */
2426
2427                 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
2428                 Temporal::Beats latest   = Temporal::Beats();
2429
2430                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2431                         if ((*i)->note()->end_time() > latest) {
2432                                 latest = (*i)->note()->end_time();
2433                         }
2434                         if ((*i)->note()->time() < earliest) {
2435                                 earliest = (*i)->note()->time();
2436                         }
2437                 }
2438
2439                 if (ev->note()->end_time() > latest) {
2440                         latest = ev->note()->end_time();
2441                 }
2442
2443                 if (ev->note()->time() < earliest) {
2444                         earliest = ev->note()->time();
2445                 }
2446
2447                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2448
2449                         /* find notes entirely within OR spanning the earliest..latest range */
2450
2451                         if ((i->first->time() >= earliest && i->first->end_time() <= latest) ||
2452                             (i->first->time() <= earliest && i->first->end_time() >= latest)) {
2453                                 add_to_selection (i->second);
2454                         }
2455                 }
2456         }
2457 }
2458
2459 void
2460 MidiRegionView::note_deselected(NoteBase* ev)
2461 {
2462         remove_from_selection (ev);
2463 }
2464
2465 void
2466 MidiRegionView::update_drag_selection(samplepos_t start, samplepos_t end, double gy0, double gy1, bool extend)
2467 {
2468         PublicEditor& editor = trackview.editor();
2469
2470         // Convert to local coordinates
2471         const samplepos_t p  = _region->position();
2472         const double     y  = midi_view()->y_position();
2473         const double     x0 = editor.sample_to_pixel(max((samplepos_t)0, start - p));
2474         const double     x1 = editor.sample_to_pixel(max((samplepos_t)0, end - p));
2475         const double     y0 = max(0.0, gy0 - y);
2476         const double     y1 = max(0.0, gy1 - y);
2477
2478         // TODO: Make this faster by storing the last updated selection rect, and only
2479         // adjusting things that are in the area that appears/disappeared.
2480         // We probably need a tree to be able to find events in O(log(n)) time.
2481
2482         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2483                 if (i->second->x0() < x1 && i->second->x1() > x0 && i->second->y0() < y1 && i->second->y1() > y0) {
2484                         // Rectangles intersect
2485                         if (!i->second->selected()) {
2486                                 add_to_selection (i->second);
2487                         }
2488                 } else if (i->second->selected() && !extend) {
2489                         // Rectangles do not intersect
2490                         remove_from_selection (i->second);
2491                 }
2492         }
2493
2494         typedef RouteTimeAxisView::AutomationTracks ATracks;
2495         typedef std::list<Selectable*>              Selectables;
2496
2497         /* Add control points to selection. */
2498         const ATracks& atracks = midi_view()->automation_tracks();
2499         Selectables    selectables;
2500         editor.get_selection().clear_points();
2501         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2502                 a->second->get_selectables(start, end, gy0, gy1, selectables);
2503                 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2504                         ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2505                         if (cp) {
2506                                 editor.get_selection().add(cp);
2507                         }
2508                 }
2509                 a->second->set_selected_points(editor.get_selection().points);
2510         }
2511 }
2512
2513 void
2514 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2515 {
2516         if (y1 > y2) {
2517                 swap (y1, y2);
2518         }
2519
2520         // TODO: Make this faster by storing the last updated selection rect, and only
2521         // adjusting things that are in the area that appears/disappeared.
2522         // We probably need a tree to be able to find events in O(log(n)) time.
2523
2524         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2525                 if ((i->second->y1() >= y1 && i->second->y1() <= y2)) {
2526                         // within y- (note-) range
2527                         if (!i->second->selected()) {
2528                                 add_to_selection (i->second);
2529                         }
2530                 } else if (i->second->selected() && !extend) {
2531                         remove_from_selection (i->second);
2532                 }
2533         }
2534 }
2535
2536 void
2537 MidiRegionView::remove_from_selection (NoteBase* ev)
2538 {
2539         Selection::iterator i = _selection.find (ev);
2540
2541         if (i != _selection.end()) {
2542                 _selection.erase (i);
2543                 if (_selection.empty() && _grabbed_keyboard) {
2544                         // Ungrab keyboard
2545                         Keyboard::magic_widget_drop_focus();
2546                         _grabbed_keyboard = false;
2547                 }
2548         }
2549
2550         ev->set_selected (false);
2551         ev->hide_velocity ();
2552
2553         if (_selection.empty()) {
2554                 PublicEditor& editor (trackview.editor());
2555                 editor.get_selection().remove (this);
2556         }
2557 }
2558
2559 void
2560 MidiRegionView::add_to_selection (NoteBase* ev)
2561 {
2562         const bool selection_was_empty = _selection.empty();
2563
2564         if (_selection.insert (ev).second) {
2565                 ev->set_selected (true);
2566                 start_playing_midi_note ((ev)->note());
2567                 if (selection_was_empty && _entered) {
2568                         // Grab keyboard for moving notes with arrow keys
2569                         Keyboard::magic_widget_grab_focus();
2570                         _grabbed_keyboard = true;
2571                 }
2572         }
2573
2574         if (selection_was_empty) {
2575                 PublicEditor& editor (trackview.editor());
2576                 editor.get_selection().add (this);
2577         }
2578 }
2579
2580 Temporal::Beats
2581 MidiRegionView::earliest_in_selection ()
2582 {
2583         Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
2584
2585         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2586                 if ((*i)->note()->time() < earliest) {
2587                         earliest = (*i)->note()->time();
2588                 }
2589         }
2590
2591         return earliest;
2592 }
2593
2594 void
2595 MidiRegionView::move_selection(double dx_qn, double dy, double cumulative_dy)
2596 {
2597         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2598         Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
2599         TempoMap& tmap (editor->session()->tempo_map());
2600         PossibleChord to_play;
2601         Temporal::Beats earliest = earliest_in_selection();
2602
2603         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2604                 NoteBase* n = *i;
2605                 if (n->note()->time() == earliest) {
2606                         to_play.push_back (n->note());
2607                 }
2608                 double const note_time_qn = session_relative_qn (n->note()->time().to_double());
2609                 double dx = 0.0;
2610                 if (midi_view()->note_mode() == Sustained) {
2611                         dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2612                                 - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
2613                 } else {
2614                         /* Hit::x0() is offset by _position.x, unlike Note::x0() */
2615                         Hit* hit = dynamic_cast<Hit*>(n);
2616                         if (hit) {
2617                                 dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2618                                         - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
2619                         }
2620                 }
2621
2622                 (*i)->move_event(dx, dy);
2623
2624                 /* update length */
2625                 if (midi_view()->note_mode() == Sustained) {
2626                         Note* sus = dynamic_cast<Note*> (*i);
2627                         double const len_dx = editor->sample_to_pixel_unrounded (
2628                                 tmap.sample_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
2629
2630                         sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
2631                 }
2632         }
2633
2634         if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2635
2636                 if (to_play.size() > 1) {
2637
2638                         PossibleChord shifted;
2639
2640                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2641                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2642                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2643                                 shifted.push_back (moved_note);
2644                         }
2645
2646                         start_playing_midi_chord (shifted);
2647
2648                 } else if (!to_play.empty()) {
2649
2650                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2651                         moved_note->set_note (moved_note->note() + cumulative_dy);
2652                         start_playing_midi_note (moved_note);
2653                 }
2654         }
2655 }
2656
2657 NoteBase*
2658 MidiRegionView::copy_selection (NoteBase* primary)
2659 {
2660         _copy_drag_events.clear ();
2661
2662         if (_selection.empty()) {
2663                 return 0;
2664         }
2665
2666         NoteBase* note;
2667         NoteBase* ret = 0;
2668
2669         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2670                 boost::shared_ptr<NoteType> g (new NoteType (*((*i)->note())));
2671                 if (midi_view()->note_mode() == Sustained) {
2672                         Note* n = new Note (*this, _note_group, g);
2673                         update_sustained (n, false);
2674                         note = n;
2675                 } else {
2676                         Hit* h = new Hit (*this, _note_group, 10, g);
2677                         update_hit (h, false);
2678                         note = h;
2679                 }
2680
2681                 if ((*i) == primary) {
2682                         ret = note;
2683                 }
2684
2685                 _copy_drag_events.push_back (note);
2686         }
2687
2688         return ret;
2689 }
2690
2691 void
2692 MidiRegionView::move_copies (double dx_qn, double dy, double cumulative_dy)
2693 {
2694         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2695         Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
2696         TempoMap& tmap (editor->session()->tempo_map());
2697         PossibleChord to_play;
2698         Temporal::Beats earliest = earliest_in_selection();
2699
2700         for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2701                 NoteBase* n = *i;
2702                 if (n->note()->time() == earliest) {
2703                         to_play.push_back (n->note());
2704                 }
2705                 double const note_time_qn = session_relative_qn (n->note()->time().to_double());
2706                 double dx = 0.0;
2707                 if (midi_view()->note_mode() == Sustained) {
2708                         dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2709                                 - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
2710                 } else {
2711                         Hit* hit = dynamic_cast<Hit*>(n);
2712                         if (hit) {
2713                                 dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2714                                         - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
2715                         }
2716                 }
2717
2718                 (*i)->move_event(dx, dy);
2719
2720                 if (midi_view()->note_mode() == Sustained) {
2721                         Note* sus = dynamic_cast<Note*> (*i);
2722                         double const len_dx = editor->sample_to_pixel_unrounded (
2723                                 tmap.sample_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
2724
2725                         sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
2726                 }
2727         }
2728
2729         if (dy && !_copy_drag_events.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2730
2731                 if (to_play.size() > 1) {
2732
2733                         PossibleChord shifted;
2734
2735                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2736                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2737                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2738                                 shifted.push_back (moved_note);
2739                         }
2740
2741                         start_playing_midi_chord (shifted);
2742
2743                 } else if (!to_play.empty()) {
2744
2745                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2746                         moved_note->set_note (moved_note->note() + cumulative_dy);
2747                         start_playing_midi_note (moved_note);
2748                 }
2749         }
2750 }
2751
2752 void
2753 MidiRegionView::note_dropped(NoteBase *, double d_qn, int8_t dnote, bool copy)
2754 {
2755         uint8_t lowest_note_in_selection  = 127;
2756         uint8_t highest_note_in_selection = 0;
2757         uint8_t highest_note_difference   = 0;
2758
2759         if (!copy) {
2760                 // find highest and lowest notes first
2761
2762                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2763                         uint8_t pitch = (*i)->note()->note();
2764                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2765                         highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2766                 }
2767
2768                 /*
2769                   cerr << "dnote: " << (int) dnote << endl;
2770                   cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2771                   << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2772                   cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2773                   << int(highest_note_in_selection) << endl;
2774                   cerr << "selection size: " << _selection.size() << endl;
2775                   cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2776                 */
2777
2778                 // Make sure the note pitch does not exceed the MIDI standard range
2779                 if (highest_note_in_selection + dnote > 127) {
2780                         highest_note_difference = highest_note_in_selection - 127;
2781                 }
2782
2783                 start_note_diff_command (_("move notes"));
2784
2785                 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2786
2787                         Temporal::Beats new_time = Temporal::Beats ((*i)->note()->time().to_double() + d_qn);
2788
2789                         if (new_time < 0) {
2790                                 continue;
2791                         }
2792
2793                         note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2794
2795                         uint8_t original_pitch = (*i)->note()->note();
2796                         uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2797
2798                         // keep notes in standard midi range
2799                         clamp_to_0_127(new_pitch);
2800
2801                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2802                         highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2803
2804                         note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2805                 }
2806         } else {
2807
2808                 clear_editor_note_selection ();
2809
2810                 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2811                         uint8_t pitch = (*i)->note()->note();
2812                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2813                         highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2814                 }
2815
2816                 // Make sure the note pitch does not exceed the MIDI standard range
2817                 if (highest_note_in_selection + dnote > 127) {
2818                         highest_note_difference = highest_note_in_selection - 127;
2819                 }
2820
2821                 start_note_diff_command (_("copy notes"));
2822
2823                 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) {
2824
2825                         /* update time */
2826                         Temporal::Beats new_time = Temporal::Beats ((*i)->note()->time().to_double() + d_qn);
2827
2828                         if (new_time < 0) {
2829                                 continue;
2830                         }
2831
2832                         (*i)->note()->set_time (new_time);
2833
2834                         /* update pitch */
2835
2836                         uint8_t original_pitch = (*i)->note()->note();
2837                         uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2838
2839                         (*i)->note()->set_note (new_pitch);
2840
2841                         // keep notes in standard midi range
2842                         clamp_to_0_127(new_pitch);
2843
2844                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2845                         highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2846
2847                         note_diff_add_note ((*i)->note(), true);
2848
2849                         delete *i;
2850                 }
2851
2852                 _copy_drag_events.clear ();
2853         }
2854
2855         apply_diff (false, copy);
2856
2857         // care about notes being moved beyond the upper/lower bounds on the canvas
2858         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
2859             highest_note_in_selection > midi_stream_view()->highest_note()) {
2860                 midi_stream_view()->set_note_range (MidiStreamView::ContentsRange);
2861         }
2862 }
2863
2864 /** @param x Pixel relative to the region position.
2865  *  @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
2866  *  Used for inverting the snap logic with key modifiers and snap delta calculation.
2867  *  @return Snapped sample relative to the region position.
2868  */
2869 samplepos_t
2870 MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
2871 {
2872         PublicEditor& editor (trackview.editor());
2873         return snap_sample_to_sample (editor.pixel_to_sample (x), ensure_snap).sample;
2874 }
2875
2876 /** @param x Pixel relative to the region position.
2877  *  @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
2878  *  @return Snapped pixel relative to the region position.
2879  */
2880 double
2881 MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
2882 {
2883         return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
2884 }
2885
2886 double
2887 MidiRegionView::get_position_pixels()
2888 {
2889         samplepos_t region_sample = get_position();
2890         return trackview.editor().sample_to_pixel(region_sample);
2891 }
2892
2893 double
2894 MidiRegionView::get_end_position_pixels()
2895 {
2896         samplepos_t sample = get_position() + get_duration ();
2897         return trackview.editor().sample_to_pixel(sample);
2898 }
2899
2900 samplepos_t
2901 MidiRegionView::source_beats_to_absolute_samples(Temporal::Beats beats) const
2902 {
2903         /* the time converter will return the sample corresponding to `beats'
2904            relative to the start of the source. The start of the source
2905            is an implied position given by region->position - region->start
2906         */
2907         const samplepos_t source_start = _region->position() - _region->start();
2908         return  source_start +  _source_relative_time_converter.to (beats);
2909 }
2910
2911 Temporal::Beats
2912 MidiRegionView::absolute_samples_to_source_beats(samplepos_t samples) const
2913 {
2914         /* the `samples' argument needs to be converted into a sample count
2915            relative to the start of the source before being passed in to the
2916            converter.
2917         */
2918         const samplepos_t source_start = _region->position() - _region->start();
2919         return  _source_relative_time_converter.from (samples - source_start);
2920 }
2921
2922 samplepos_t
2923 MidiRegionView::region_beats_to_region_samples(Temporal::Beats beats) const
2924 {
2925         return _region_relative_time_converter.to(beats);
2926 }
2927
2928 Temporal::Beats
2929 MidiRegionView::region_samples_to_region_beats(samplepos_t samples) const
2930 {
2931         return _region_relative_time_converter.from(samples);
2932 }
2933
2934 double
2935 MidiRegionView::region_samples_to_region_beats_double (samplepos_t samples) const
2936 {
2937         return _region_relative_time_converter_double.from(samples);
2938 }
2939
2940 void
2941 MidiRegionView::begin_resizing (bool /*at_front*/)
2942 {
2943         _resize_data.clear();
2944
2945         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2946                 Note *note = dynamic_cast<Note*> (*i);
2947
2948                 // only insert CanvasNotes into the map
2949                 if (note) {
2950                         NoteResizeData *resize_data = new NoteResizeData();
2951                         resize_data->note = note;
2952
2953                         // create a new SimpleRect from the note which will be the resize preview
2954                         ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2955                                                                                             ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2956
2957                         // calculate the colors: get the color settings
2958                         uint32_t fill_color = NoteBase::meter_style_fill_color (note->note()->velocity(), true);
2959
2960                         // make the resize preview notes more transparent and bright
2961                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2962
2963                         // calculate color based on note velocity
2964                         resize_rect->set_fill_color (UINT_INTERPOLATE(
2965                                 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2966                                 fill_color,
2967                                 0.85));
2968
2969                         resize_rect->set_outline_color (NoteBase::calculate_outline (
2970                                                                 UIConfiguration::instance().color ("midi note selected outline")));
2971
2972                         resize_data->resize_rect = resize_rect;
2973                         _resize_data.push_back(resize_data);
2974                 }
2975         }
2976 }
2977
2978 /** Update resizing notes while user drags.
2979  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2980  * @param at_front which end of the note (true == note on, false == note off)
2981  * @param delta_x change in mouse position since the start of the drag
2982  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2983  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2984  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2985  * as the \a primary note.
2986  * @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
2987  * @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
2988  */
2989 void
2990 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2991 {
2992         TempoMap& tmap (trackview.session()->tempo_map());
2993         bool cursor_set = false;
2994         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
2995
2996         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2997                 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2998                 Note* canvas_note = (*i)->note;
2999                 double current_x;
3000
3001                 if (at_front) {
3002                         if (relative) {
3003                                 current_x = canvas_note->x0() + delta_x + snap_delta;
3004                         } else {
3005                                 current_x = primary->x0() + delta_x + snap_delta;
3006                         }
3007                 } else {
3008                         if (relative) {
3009                                 current_x = canvas_note->x1() + delta_x + snap_delta;
3010                         } else {
3011                                 current_x = primary->x1() + delta_x + snap_delta;
3012                         }
3013                 }
3014
3015                 if (current_x < 0) {
3016                         /* This works even with snapping because RegionView::snap_sample_to_sample()
3017                          * snaps forward if the snapped sample is before the beginning of the region
3018                          */
3019                         current_x = 0;
3020                 }
3021                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
3022                         current_x = trackview.editor().sample_to_pixel(_region->length());
3023                 }
3024
3025                 if (at_front) {
3026                         if (with_snap) {
3027                                 resize_rect->set_x0 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
3028                         } else {
3029                                 resize_rect->set_x0 (current_x - snap_delta);
3030                         }
3031                         resize_rect->set_x1 (canvas_note->x1());
3032                 } else {
3033                         if (with_snap) {
3034                                 resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
3035                         } else {
3036                                 resize_rect->set_x1 (current_x - snap_delta);
3037                         }
3038                         resize_rect->set_x0 (canvas_note->x0());
3039                 }
3040
3041                 if (!cursor_set) {
3042                         /* Convert snap delta from pixels to beats. */
3043                         samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
3044                         double snap_delta_beats = 0.0;
3045                         int sign = 1;
3046
3047                         /* negative beat offsets aren't allowed */
3048                         if (snap_delta_samps > 0) {
3049                                 snap_delta_beats = region_samples_to_region_beats_double (snap_delta_samps);
3050                         } else if (snap_delta_samps < 0) {
3051                                 snap_delta_beats = region_samples_to_region_beats_double (- snap_delta_samps);
3052                                 sign = -1;
3053                         }
3054
3055                         double  snapped_x;
3056                         int32_t divisions = 0;
3057
3058                         if (with_snap) {
3059                                 snapped_x = snap_pixel_to_sample (current_x, ensure_snap);
3060                                 divisions = trackview.editor().get_grid_music_divisions (0);
3061                         } else {
3062                                 snapped_x = trackview.editor ().pixel_to_sample (current_x);
3063                         }
3064
3065                         const Temporal::Beats beats = Temporal::Beats (tmap.exact_beat_at_sample (snapped_x + midi_region()->position(), divisions)
3066                                                                        - midi_region()->beat())
3067                                                       + midi_region()->start_beats();
3068
3069                         Temporal::Beats len         = Temporal::Beats();
3070
3071                         if (at_front) {
3072                                 if (beats < canvas_note->note()->end_time()) {
3073                                         len = canvas_note->note()->time() - beats + (sign * snap_delta_beats);
3074                                         len += canvas_note->note()->length();
3075                                 }
3076                         } else {
3077                                 if (beats >= canvas_note->note()->time()) {
3078                                         len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
3079                                 }
3080                         }
3081
3082                         len = std::max(Temporal::Beats(1 / 512.0), len);
3083
3084                         char buf[16];
3085                         snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
3086                         show_verbose_cursor (buf, 0, 0);
3087
3088                         cursor_set = true;
3089
3090                         trackview.editor().set_snapped_cursor_position ( snapped_x + midi_region()->position() );
3091                 }
3092
3093         }
3094 }
3095
3096
3097 /** Finish resizing notes when the user releases the mouse button.
3098  *  Parameters the same as for \a update_resizing().
3099  */
3100 void
3101 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
3102 {
3103         _note_diff_command = _model->new_note_diff_command (_("resize notes"));
3104         TempoMap& tmap (trackview.session()->tempo_map());
3105
3106         /* XX why doesn't snap_pixel_to_sample() handle this properly? */
3107         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
3108
3109         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
3110                 Note*  canvas_note = (*i)->note;
3111                 ArdourCanvas::Rectangle*  resize_rect = (*i)->resize_rect;
3112
3113                 /* Get the new x position for this resize, which is in pixels relative
3114                  * to the region position.
3115                  */
3116
3117                 double current_x;
3118
3119                 if (at_front) {
3120                         if (relative) {
3121                                 current_x = canvas_note->x0() + delta_x + snap_delta;
3122                         } else {
3123                                 current_x = primary->x0() + delta_x + snap_delta;
3124                         }
3125                 } else {
3126                         if (relative) {
3127                                 current_x = canvas_note->x1() + delta_x + snap_delta;
3128                         } else {
3129                                 current_x = primary->x1() + delta_x + snap_delta;
3130                         }
3131                 }
3132
3133                 if (current_x < 0) {
3134                         current_x = 0;
3135                 }
3136                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
3137                         current_x = trackview.editor().sample_to_pixel(_region->length());
3138                 }
3139
3140                 /* Convert snap delta from pixels to beats with sign. */
3141                 samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
3142                 double snap_delta_beats = 0.0;
3143                 int sign = 1;
3144
3145                 if (snap_delta_samps > 0) {
3146                         snap_delta_beats = region_samples_to_region_beats_double (snap_delta_samps);
3147                 } else if (snap_delta_samps < 0) {
3148                         snap_delta_beats = region_samples_to_region_beats_double ( - snap_delta_samps);
3149                         sign = -1;
3150                 }
3151
3152                 uint32_t divisions = 0;
3153                 /* Convert the new x position to a sample within the source */
3154                 samplepos_t current_fr;
3155                 if (with_snap) {
3156                         current_fr = snap_pixel_to_sample (current_x, ensure_snap);
3157                         divisions = trackview.editor().get_grid_music_divisions (0);
3158                 } else {
3159                         current_fr = trackview.editor().pixel_to_sample (current_x);
3160                 }
3161
3162                 /* and then to beats */
3163                 const double e_qaf = tmap.exact_qn_at_sample (current_fr + midi_region()->position(), divisions);
3164                 const double quarter_note_start = _region->quarter_note() - midi_region()->start_beats();
3165                 const Temporal::Beats x_beats = Temporal::Beats (e_qaf - quarter_note_start);
3166
3167                 if (at_front && x_beats < canvas_note->note()->end_time()) {
3168                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
3169                         Temporal::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
3170                         len += canvas_note->note()->length();
3171
3172                         if (!!len) {
3173                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
3174                         }
3175                 }
3176
3177                 if (!at_front) {
3178                         Temporal::Beats len = std::max(Temporal::Beats(1 / 512.0),
3179                                                      x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
3180                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
3181                 }
3182
3183                 delete resize_rect;
3184                 delete (*i);
3185         }
3186
3187         _resize_data.clear();
3188         apply_diff(true);
3189 }
3190
3191 void
3192 MidiRegionView::abort_resizing ()
3193 {
3194         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
3195                 delete (*i)->resize_rect;
3196                 delete *i;
3197         }
3198
3199         _resize_data.clear ();
3200 }
3201
3202 void
3203 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
3204 {
3205         uint8_t new_velocity;
3206
3207         if (relative) {
3208                 new_velocity = event->note()->velocity() + velocity;
3209                 clamp_to_0_127(new_velocity);
3210         } else {
3211                 new_velocity = velocity;
3212         }
3213
3214         event->set_selected (event->selected()); // change color
3215
3216         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
3217 }
3218
3219 void
3220 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
3221 {
3222         uint8_t new_note;
3223
3224         if (relative) {
3225                 new_note = event->note()->note() + note;
3226         } else {
3227                 new_note = note;
3228         }
3229
3230         clamp_to_0_127 (new_note);
3231         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
3232 }
3233
3234 void
3235 MidiRegionView::trim_note (NoteBase* event, Temporal::Beats front_delta, Temporal::Beats end_delta)
3236 {
3237         bool change_start = false;
3238         bool change_length = false;
3239         Temporal::Beats new_start;
3240         Temporal::Beats new_length;
3241
3242         /* NOTE: the semantics of the two delta arguments are slightly subtle:
3243
3244            front_delta: if positive - move the start of the note later in time (shortening it)
3245            if negative - move the start of the note earlier in time (lengthening it)
3246
3247            end_delta:   if positive - move the end of the note later in time (lengthening it)
3248            if negative - move the end of the note earlier in time (shortening it)
3249         */
3250
3251         if (!!front_delta) {
3252                 if (front_delta < 0) {
3253
3254                         if (event->note()->time() < -front_delta) {
3255                                 new_start = Temporal::Beats();
3256                         } else {
3257                                 new_start = event->note()->time() + front_delta; // moves earlier
3258                         }
3259
3260                         /* start moved toward zero, so move the end point out to where it used to be.
3261                            Note that front_delta is negative, so this increases the length.
3262                         */
3263
3264                         new_length = event->note()->length() - front_delta;
3265                         change_start = true;
3266                         change_length = true;
3267
3268                 } else {
3269
3270                         Temporal::Beats new_pos = event->note()->time() + front_delta;
3271
3272                         if (new_pos < event->note()->end_time()) {
3273                                 new_start = event->note()->time() + front_delta;
3274                                 /* start moved toward the end, so move the end point back to where it used to be */
3275                                 new_length = event->note()->length() - front_delta;
3276                                 change_start = true;
3277                                 change_length = true;
3278                         }
3279                 }
3280
3281         }
3282
3283         if (!!end_delta) {
3284                 bool can_change = true;
3285                 if (end_delta < 0) {
3286                         if (event->note()->length() < -end_delta) {
3287                                 can_change = false;
3288                         }
3289                 }
3290
3291                 if (can_change) {
3292                         new_length = event->note()->length() + end_delta;
3293                         change_length = true;
3294                 }
3295         }
3296
3297         if (change_start) {
3298                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
3299         }
3300
3301         if (change_length) {
3302                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
3303         }
3304 }
3305
3306 void
3307 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
3308 {
3309         uint8_t new_channel;
3310
3311         if (relative) {
3312                 if (chn < 0.0) {
3313                         if (event->note()->channel() < -chn) {
3314                                 new_channel = 0;
3315                         } else {
3316                                 new_channel = event->note()->channel() + chn;
3317                         }
3318                 } else {
3319                         new_channel = event->note()->channel() + chn;
3320                 }
3321         } else {
3322                 new_channel = (uint8_t) chn;
3323         }
3324
3325         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
3326 }
3327
3328 void
3329 MidiRegionView::change_note_time (NoteBase* event, Temporal::Beats delta, bool relative)
3330 {
3331         Temporal::Beats new_time;
3332
3333         if (relative) {
3334                 if (delta < 0.0) {
3335                         if (event->note()->time() < -delta) {
3336                                 new_time = Temporal::Beats();
3337                         } else {
3338                                 new_time = event->note()->time() + delta;
3339                         }
3340                 } else {
3341                         new_time = event->note()->time() + delta;
3342                 }
3343         } else {
3344                 new_time = delta;
3345         }
3346
3347         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3348 }
3349
3350 void
3351 MidiRegionView::change_note_length (NoteBase* event, Temporal::Beats t)
3352 {
3353         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3354 }
3355
3356 void
3357 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3358 {
3359         int8_t delta;
3360         int8_t value = 0;
3361
3362         if (_selection.empty()) {
3363                 return;
3364         }
3365
3366         if (fine) {
3367                 delta = 1;
3368         } else {
3369                 delta = 10;
3370         }
3371
3372         if (!up) {
3373                 delta = -delta;
3374         }
3375
3376         if (!allow_smush) {
3377                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3378                         if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3379                                 goto cursor_label;
3380                         }
3381                 }
3382         }
3383
3384         start_note_diff_command (_("change velocities"));
3385
3386         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3387                 Selection::iterator next = i;
3388                 ++next;
3389
3390                 if (all_together) {
3391                         if (i == _selection.begin()) {
3392                                 change_note_velocity (*i, delta, true);
3393                                 value = (*i)->note()->velocity() + delta;
3394                         } else {
3395                                 change_note_velocity (*i, value, false);
3396                         }
3397
3398                 } else {
3399                         change_note_velocity (*i, delta, true);
3400                 }
3401
3402                 i = next;
3403         }
3404
3405         apply_diff();
3406
3407   cursor_label:
3408         if (!_selection.empty()) {
3409                 char buf[24];
3410                 snprintf (buf, sizeof (buf), "Vel %d",
3411                           (int) (*_selection.begin())->note()->velocity());
3412                 show_verbose_cursor (buf, 10, 10);
3413         }
3414 }
3415
3416
3417 void
3418 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3419 {
3420         if (_selection.empty()) {
3421                 return;
3422         }
3423
3424         int8_t delta;
3425
3426         if (fine) {
3427                 delta = 1;
3428         } else {
3429                 delta = 12;
3430         }
3431
3432         if (!up) {
3433                 delta = -delta;
3434         }
3435
3436         if (!allow_smush) {
3437                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3438                         if (!up) {
3439                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3440                                         return;
3441                                 }
3442                         } else {
3443                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
3444                                         return;
3445                                 }
3446                         }
3447                 }
3448         }
3449
3450         start_note_diff_command (_("transpose"));
3451
3452         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3453                 Selection::iterator next = i;
3454                 ++next;
3455                 change_note_note (*i, delta, true);
3456                 i = next;
3457         }
3458
3459         apply_diff ();
3460 }
3461
3462 void
3463 MidiRegionView::change_note_lengths (bool fine, bool shorter, Temporal::Beats delta, bool start, bool end)
3464 {
3465         if (!delta) {
3466                 if (fine) {
3467                         delta = Temporal::Beats(1.0/128.0);
3468                 } else {
3469                         /* grab the current grid distance */
3470                         delta = get_grid_beats(_region->position());
3471                 }
3472         }
3473
3474         if (shorter) {
3475                 delta = -delta;
3476         }
3477
3478         start_note_diff_command (_("change note lengths"));
3479
3480         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3481                 Selection::iterator next = i;
3482                 ++next;
3483
3484                 /* note the negation of the delta for start */
3485
3486                 trim_note (*i,
3487                            (start ? -delta : Temporal::Beats()),
3488                            (end   ? delta  : Temporal::Beats()));
3489                 i = next;
3490         }
3491
3492         apply_diff ();
3493
3494 }
3495
3496 void
3497 MidiRegionView::nudge_notes (bool forward, bool fine)
3498 {
3499         if (_selection.empty()) {
3500                 return;
3501         }
3502
3503         /* pick a note as the point along the timeline to get the nudge distance.
3504            its not necessarily the earliest note, so we may want to pull the notes out
3505            into a vector and sort before using the first one.
3506         */
3507
3508         const samplepos_t ref_point = source_beats_to_absolute_samples ((*(_selection.begin()))->note()->time());
3509         Temporal::Beats  delta;
3510
3511         if (!fine) {
3512
3513                 /* non-fine, move by 1 bar regardless of snap */
3514                 delta = Temporal::Beats(trackview.session()->tempo_map().meter_at_sample (ref_point).divisions_per_bar());
3515
3516         } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3517
3518                 /* grid is off - use nudge distance */
3519
3520                 samplepos_t       unused;
3521                 const samplecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3522                 delta = region_samples_to_region_beats (fabs ((double)distance));
3523
3524         } else {
3525
3526                 /* use grid */
3527
3528                 MusicSample next_pos (ref_point, 0);
3529                 if (forward) {
3530                         if (max_samplepos - 1 < next_pos.sample) {
3531                                 next_pos.sample += 1;
3532                         }
3533                 } else {
3534                         if (next_pos.sample == 0) {
3535                                 return;
3536                         }
3537                         next_pos.sample -= 1;
3538                 }
3539
3540                 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), SnapToGrid_Unscaled, false);
3541                 const samplecnt_t distance = ref_point - next_pos.sample;
3542                 delta = region_samples_to_region_beats (fabs ((double)distance));
3543         }
3544
3545         if (!delta) {
3546                 return;
3547         }
3548
3549         if (!forward) {
3550                 delta = -delta;
3551         }
3552
3553         start_note_diff_command (_("nudge"));
3554
3555         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3556                 Selection::iterator next = i;
3557                 ++next;
3558                 change_note_time (*i, delta, true);
3559                 i = next;
3560         }
3561
3562         apply_diff ();
3563 }
3564
3565 void
3566 MidiRegionView::change_channel(uint8_t channel)
3567 {
3568         start_note_diff_command(_("change channel"));
3569         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3570                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3571         }
3572
3573         apply_diff();
3574 }
3575
3576
3577 void
3578 MidiRegionView::note_entered(NoteBase* ev)
3579 {
3580         _entered_note = ev;
3581
3582         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3583
3584         if (_mouse_state == SelectTouchDragging) {
3585
3586                 note_selected (ev, true);
3587
3588         } else if (editor->current_mouse_mode() == MouseContent) {
3589
3590                 remove_ghost_note ();
3591                 show_verbose_cursor (ev->note ());
3592
3593         } else if (editor->current_mouse_mode() == MouseDraw) {
3594
3595                 remove_ghost_note ();
3596                 show_verbose_cursor (ev->note ());
3597         }
3598 }
3599
3600 void
3601 MidiRegionView::note_left (NoteBase*)
3602 {
3603         _entered_note = 0;
3604
3605         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3606                 (*i)->hide_velocity ();
3607         }
3608
3609         hide_verbose_cursor ();
3610 }
3611
3612 void
3613 MidiRegionView::patch_entered (PatchChange* p)
3614 {
3615         ostringstream s;
3616         s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3617           << instrument_info().get_patch_name_without (p->patch()->bank(), p->patch()->program(), p->patch()->channel()) << '\n'
3618           << _("Channel ") << ((int) p->patch()->channel() + 1);
3619         show_verbose_cursor (s.str(), 10, 20);
3620         p->item().grab_focus();
3621 }
3622
3623 void
3624 MidiRegionView::patch_left (PatchChange *)
3625 {
3626         hide_verbose_cursor ();
3627         /* focus will transfer back via the enter-notify event sent to this
3628          * midi region view.
3629          */
3630 }
3631
3632 void
3633 MidiRegionView::sysex_entered (SysEx* p)
3634 {
3635         // ostringstream s;
3636         // CAIROCANVAS
3637         // need a way to extract text from p->_flag->_text
3638         // s << p->text();
3639         // show_verbose_cursor (s.str(), 10, 20);
3640         p->item().grab_focus();
3641 }
3642
3643 void
3644 MidiRegionView::sysex_left (SysEx *)
3645 {
3646         hide_verbose_cursor ();
3647         /* focus will transfer back via the enter-notify event sent to this
3648          * midi region view.
3649          */
3650 }
3651
3652 void
3653 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3654 {
3655         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3656         Editing::MouseMode mm = editor->current_mouse_mode();
3657         bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3658
3659         Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3660         if (can_set_cursor && ctx) {
3661                 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3662                         ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3663                 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3664                         ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3665                 } else {
3666                         ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3667                 }
3668         }
3669 }
3670
3671 uint32_t
3672 MidiRegionView::get_fill_color() const
3673 {
3674         const std::string mod_name = (_dragging ? "dragging region" :
3675                                       trackview.editor().internal_editing() ? "editable region" :
3676                                       "midi frame base");
3677         if (_selected) {
3678                 return UIConfiguration::instance().color_mod ("selected region base", mod_name);
3679         } else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) &&
3680                    !UIConfiguration::instance().get_color_regions_using_track_color()) {
3681                 return UIConfiguration::instance().color_mod ("midi frame base", mod_name);
3682         }
3683         return UIConfiguration::instance().color_mod (fill_color, mod_name);
3684 }
3685
3686 void
3687 MidiRegionView::midi_channel_mode_changed ()
3688 {
3689         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3690         uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3691         ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3692
3693         if (mode == ForceChannel) {
3694                 mask = 0xFFFF; // Show all notes as active (below)
3695         }
3696
3697         // Update notes for selection
3698         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3699                 i->second->on_channel_selection_change (mask);
3700         }
3701
3702         _patch_changes.clear ();
3703         display_patch_changes ();
3704 }
3705
3706 void
3707 MidiRegionView::instrument_settings_changed ()
3708 {
3709         redisplay_model();
3710 }
3711
3712 void
3713 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3714 {
3715         if (_selection.empty()) {
3716                 return;
3717         }
3718
3719         PublicEditor& editor (trackview.editor());
3720
3721         switch (op) {
3722         case Delete:
3723                 /* XXX what to do ? */
3724                 break;
3725         case Cut:
3726         case Copy:
3727                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3728                 break;
3729         default:
3730                 break;
3731         }
3732
3733         if (op != Copy) {
3734
3735                 start_note_diff_command();
3736
3737                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3738                         switch (op) {
3739                         case Copy:
3740                                 break;
3741                         case Delete:
3742                         case Cut:
3743                         case Clear:
3744                                 note_diff_remove_note (*i);
3745                                 break;
3746                         }
3747                 }
3748
3749                 apply_diff();
3750         }
3751 }
3752
3753 MidiCutBuffer*
3754 MidiRegionView::selection_as_cut_buffer () const
3755 {
3756         Notes notes;
3757
3758         for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3759                 NoteType* n = (*i)->note().get();
3760                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3761         }
3762
3763         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3764         cb->set (notes);
3765
3766         return cb;
3767 }
3768
3769 /** This method handles undo */
3770 bool
3771 MidiRegionView::paste (samplepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
3772 {
3773         bool commit = false;
3774         // Paste notes, if available
3775         MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3776         if (m != selection.midi_notes.end()) {
3777                 ctx.counts.increase_n_notes();
3778                 if (!(*m)->empty()) {
3779                         commit = true;
3780                 }
3781                 paste_internal(pos, ctx.count, ctx.times, **m);
3782         }
3783
3784         // Paste control points to automation children, if available
3785         typedef RouteTimeAxisView::AutomationTracks ATracks;
3786         const ATracks& atracks = midi_view()->automation_tracks();
3787         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3788                 if (a->second->paste(pos, selection, ctx, sub_num)) {
3789                         if(!commit) {
3790                                 trackview.editor().begin_reversible_command (Operations::paste);
3791                         }
3792                         commit = true;
3793                 }
3794         }
3795
3796         if (commit) {
3797                 trackview.editor().commit_reversible_command ();
3798         }
3799         return true;
3800 }
3801
3802 /** This method handles undo */
3803 void
3804 MidiRegionView::paste_internal (samplepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3805 {
3806         if (mcb.empty()) {
3807                 return;
3808         }
3809
3810         start_note_diff_command (_("paste"));
3811
3812         const Temporal::Beats snap_beats    = get_grid_beats(pos);
3813         const Temporal::Beats first_time    = (*mcb.notes().begin())->time();
3814         const Temporal::Beats last_time     = (*mcb.notes().rbegin())->end_time();
3815         const Temporal::Beats duration      = last_time - first_time;
3816         const Temporal::Beats snap_duration = duration.snap_to(snap_beats);
3817         const Temporal::Beats paste_offset  = snap_duration * paste_count;
3818         const Temporal::Beats quarter_note  = absolute_samples_to_source_beats(pos) + paste_offset;
3819         Temporal::Beats       end_point     = Temporal::Beats();
3820
3821         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3822                                                        first_time,
3823                                                        last_time,
3824                                                        duration, pos, _region->position(),
3825                                                        quarter_note));
3826
3827         clear_editor_note_selection ();
3828
3829         for (int n = 0; n < (int) times; ++n) {
3830
3831                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3832
3833                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3834                         copied_note->set_time (quarter_note + copied_note->time() - first_time);
3835                         copied_note->set_id (Evoral::next_event_id());
3836
3837                         /* make all newly added notes selected */
3838
3839                         note_diff_add_note (copied_note, true);
3840                         end_point = copied_note->end_time();
3841                 }
3842         }
3843
3844         /* if we pasted past the current end of the region, extend the region */
3845
3846         samplepos_t end_sample = source_beats_to_absolute_samples (end_point);
3847         samplepos_t region_end = _region->position() + _region->length() - 1;
3848
3849         if (end_sample > region_end) {
3850
3851                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_sample));
3852
3853                 _region->clear_changes ();
3854                 /* we probably need to get the snap modifier somehow to make this correct for non-musical use */
3855                 _region->set_length (end_sample - _region->position(), trackview.editor().get_grid_music_divisions (0));
3856                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3857         }
3858
3859         apply_diff (true);
3860 }
3861
3862 struct EventNoteTimeEarlyFirstComparator {
3863         bool operator() (NoteBase* a, NoteBase* b) {
3864                 return a->note()->time() < b->note()->time();
3865         }
3866 };
3867
3868 void
3869 MidiRegionView::goto_next_note (bool add_to_selection)
3870 {
3871         bool use_next = false;
3872
3873         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3874         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3875         NoteBase* first_note = 0;
3876
3877         MidiModel::ReadLock lock(_model->read_lock());
3878         MidiModel::Notes& notes (_model->notes());
3879
3880         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
3881                 NoteBase* cne = 0;
3882                 if ((cne = find_canvas_note (*n))) {
3883
3884                         if (!first_note && (channel_mask & (1 << (*n)->channel()))) {
3885                                 first_note = cne;
3886                         }
3887
3888                         if (cne->selected()) {
3889                                 use_next = true;
3890                                 continue;
3891                         } else if (use_next) {
3892                                 if (channel_mask & (1 << (*n)->channel())) {
3893                                         if (!add_to_selection) {
3894                                                 unique_select (cne);
3895                                         } else {
3896                                                 note_selected (cne, true, false);
3897                                         }
3898
3899                                         return;
3900                                 }
3901                         }
3902                 }
3903         }
3904
3905         /* use the first one */
3906
3907         if (!_events.empty() && first_note) {
3908                 unique_select (first_note);
3909         }
3910 }
3911
3912 void
3913 MidiRegionView::goto_previous_note (bool add_to_selection)
3914 {
3915         bool use_next = false;
3916
3917         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3918         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3919         NoteBase* last_note = 0;
3920
3921         MidiModel::ReadLock lock(_model->read_lock());
3922         MidiModel::Notes& notes (_model->notes());
3923
3924         for (MidiModel::Notes::reverse_iterator n = notes.rbegin(); n != notes.rend(); ++n) {
3925                 NoteBase* cne = 0;
3926                 if ((cne = find_canvas_note (*n))) {
3927
3928                         if (!last_note && (channel_mask & (1 << (*n)->channel()))) {
3929                                 last_note = cne;
3930                         }
3931
3932                         if (cne->selected()) {
3933                                 use_next = true;
3934                                 continue;
3935
3936                         } else if (use_next) {
3937                                 if (channel_mask & (1 << (*n)->channel())) {
3938                                         if (!add_to_selection) {
3939                                                 unique_select (cne);
3940                                         } else {
3941                                                 note_selected (cne, true, false);
3942                                         }
3943
3944                                         return;
3945                                 }
3946                         }
3947                 }
3948         }
3949
3950         /* use the last one */
3951
3952         if (!_events.empty() && last_note) {
3953                 unique_select (last_note);
3954         }
3955 }
3956
3957 void
3958 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3959 {
3960         bool had_selected = false;
3961
3962         /* we previously time sorted events here, but Notes is a multiset sorted by time */
3963
3964         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3965                 if (i->second->selected()) {
3966                         selected.insert (i->first);
3967                         had_selected = true;
3968                 }
3969         }
3970
3971         if (allow_all_if_none_selected && !had_selected) {
3972                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3973                         selected.insert (i->first);
3974                 }
3975         }
3976 }
3977
3978 void
3979 MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
3980 {
3981         x = std::max(0.0, x);
3982
3983         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3984
3985         _last_ghost_x = x;
3986         _last_ghost_y = y;
3987
3988         _note_group->canvas_to_item (x, y);
3989
3990         PublicEditor& editor = trackview.editor ();
3991
3992         samplepos_t const unsnapped_sample = editor.pixel_to_sample (x);
3993
3994         const int32_t divisions = editor.get_grid_music_divisions (state);
3995         const bool shift_snap = midi_view()->note_mode() != Percussive;
3996         const Temporal::Beats snapped_beats = snap_sample_to_grid_underneath (unsnapped_sample, divisions, shift_snap);
3997
3998         /* prevent Percussive mode from displaying a ghost hit at region end */
3999         if (!shift_snap && snapped_beats >= midi_region()->start_beats() + midi_region()->length_beats()) {
4000                 _ghost_note->hide();
4001                 hide_verbose_cursor ();
4002                 return;
4003         }
4004
4005         /* ghost note may have been snapped before region */
4006         if (_ghost_note && snapped_beats.to_double() < 0.0) {
4007                 _ghost_note->hide();
4008                 return;
4009
4010         } else if (_ghost_note) {
4011                 _ghost_note->show();
4012         }
4013
4014         /* calculate time in beats relative to start of source */
4015         const Temporal::Beats length = get_grid_beats(unsnapped_sample + _region->position());
4016
4017         _ghost_note->note()->set_time (snapped_beats);
4018         _ghost_note->note()->set_length (length);
4019         _ghost_note->note()->set_note (y_to_note (y));
4020         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
4021         _ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats));
4022         /* the ghost note does not appear in ghost regions, so pass false in here */
4023         update_note (_ghost_note, false);
4024
4025         show_verbose_cursor (_ghost_note->note ());
4026 }
4027
4028 void
4029 MidiRegionView::create_ghost_note (double x, double y, uint32_t state)
4030 {
4031         remove_ghost_note ();
4032
4033         boost::shared_ptr<NoteType> g (new NoteType);
4034         if (midi_view()->note_mode() == Sustained) {
4035                 _ghost_note = new Note (*this, _note_group, g);
4036         } else {
4037                 _ghost_note = new Hit (*this, _note_group, 10, g);
4038         }
4039         _ghost_note->set_ignore_events (true);
4040         _ghost_note->set_outline_color (0x000000aa);
4041         update_ghost_note (x, y, state);
4042         _ghost_note->show ();
4043
4044         show_verbose_cursor (_ghost_note->note ());
4045 }
4046
4047 void
4048 MidiRegionView::remove_ghost_note ()
4049 {
4050         delete _ghost_note;
4051         _ghost_note = 0;
4052 }
4053
4054 void
4055 MidiRegionView::hide_verbose_cursor ()
4056 {
4057         trackview.editor().verbose_cursor()->hide ();
4058         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4059         if (mtv) {
4060                 mtv->set_note_highlight (NO_MIDI_NOTE);
4061         }
4062 }
4063
4064 void
4065 MidiRegionView::snap_changed ()
4066 {
4067         if (!_ghost_note) {
4068                 return;
4069         }
4070
4071         create_ghost_note (_last_ghost_x, _last_ghost_y, 0);
4072 }
4073
4074 void
4075 MidiRegionView::drop_down_keys ()
4076 {
4077         _mouse_state = None;
4078 }
4079
4080 void
4081 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
4082 {
4083         /* XXX: This is dead code.  What was it for? */
4084
4085         double note = y_to_note(y);
4086         Events e;
4087         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4088
4089         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
4090
4091         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
4092                 get_events (e, Evoral::Sequence<Temporal::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
4093         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
4094                 get_events (e, Evoral::Sequence<Temporal::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
4095         } else {
4096                 return;
4097         }
4098
4099         bool add_mrv_selection = false;
4100
4101         if (_selection.empty()) {
4102                 add_mrv_selection = true;
4103         }
4104
4105         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
4106                 if (_selection.insert (i->second).second) {
4107                         i->second->set_selected (true);
4108                 }
4109         }
4110
4111         if (add_mrv_selection) {
4112                 PublicEditor& editor (trackview.editor());
4113                 editor.get_selection().add (this);
4114         }
4115 }
4116
4117 void
4118 MidiRegionView::color_handler ()
4119 {
4120         RegionView::color_handler ();
4121
4122         _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
4123         _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
4124
4125         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
4126                 i->second->set_selected (i->second->selected()); // will change color
4127         }
4128
4129         /* XXX probably more to do here */
4130 }
4131
4132 void
4133 MidiRegionView::enable_display (bool yn)
4134 {
4135         RegionView::enable_display (yn);
4136 }
4137
4138 void
4139 MidiRegionView::show_step_edit_cursor (Temporal::Beats pos)
4140 {
4141         if (_step_edit_cursor == 0) {
4142                 ArdourCanvas::Item* const group = get_canvas_group();
4143
4144                 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
4145                 _step_edit_cursor->set_y0 (0);
4146                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
4147                 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
4148                 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
4149         }
4150
4151         move_step_edit_cursor (pos);
4152         _step_edit_cursor->show ();
4153 }
4154
4155 void
4156 MidiRegionView::move_step_edit_cursor (Temporal::Beats pos)
4157 {
4158         _step_edit_cursor_position = pos;
4159
4160         if (_step_edit_cursor) {
4161                 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_samples (pos));
4162                 _step_edit_cursor->set_x0 (pixel);
4163                 set_step_edit_cursor_width (_step_edit_cursor_width);
4164         }
4165 }
4166
4167 void
4168 MidiRegionView::hide_step_edit_cursor ()
4169 {
4170         if (_step_edit_cursor) {
4171                 _step_edit_cursor->hide ();
4172         }
4173 }
4174
4175 void
4176 MidiRegionView::set_step_edit_cursor_width (Temporal::Beats beats)
4177 {
4178         _step_edit_cursor_width = beats;
4179
4180         if (_step_edit_cursor) {
4181                 _step_edit_cursor->set_x1 (_step_edit_cursor->x0()
4182                                            + trackview.editor().sample_to_pixel (
4183                                              region_beats_to_region_samples (_step_edit_cursor_position + beats)
4184                                              - region_beats_to_region_samples (_step_edit_cursor_position)));
4185         }
4186 }
4187
4188 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
4189  *  @param w Source that the data will end up in.
4190  */
4191 void
4192 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
4193 {
4194         if (!_active_notes) {
4195                 /* we aren't actively being recorded to */
4196                 return;
4197         }
4198
4199         boost::shared_ptr<MidiSource> src = w.lock ();
4200         if (!src || src != midi_region()->midi_source()) {
4201                 /* recorded data was not destined for our source */
4202                 return;
4203         }
4204
4205         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
4206
4207         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
4208
4209         samplepos_t back = max_samplepos;
4210
4211         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
4212                 const Evoral::Event<MidiBuffer::TimeType>& ev = *i;
4213
4214                 if (ev.is_channel_event()) {
4215                         if (get_channel_mode() == FilterChannels) {
4216                                 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
4217                                         continue;
4218                                 }
4219                         }
4220                 }
4221
4222                 /* convert from session samples to source beats */
4223                 Temporal::Beats const time_beats = _source_relative_time_converter.from(
4224                         ev.time() - src->natural_position() + _region->start());
4225
4226                 if (ev.type() == MIDI_CMD_NOTE_ON) {
4227
4228                         boost::shared_ptr<NoteType> note (new NoteType (ev.channel(), time_beats, std::numeric_limits<Temporal::Beats>::max() - time_beats, ev.note(), ev.velocity()));
4229
4230                         assert (note->end_time() == std::numeric_limits<Temporal::Beats>::max());
4231
4232                         add_note (note, true);
4233
4234                         /* fix up our note range */
4235                         if (ev.note() < _current_range_min) {
4236                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
4237                         } else if (ev.note() > _current_range_max) {
4238                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
4239                         }
4240
4241                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
4242                         resolve_note (ev.note (), time_beats);
4243                 }
4244
4245                 back = ev.time ();
4246         }
4247
4248         midi_stream_view()->check_record_layers (region(), back);
4249 }
4250
4251 void
4252 MidiRegionView::trim_front_starting ()
4253 {
4254         /* We used to eparent the note group to the region view's parent, so that it didn't change.
4255            now we update it.
4256         */
4257 }
4258
4259 void
4260 MidiRegionView::trim_front_ending ()
4261 {
4262         if (_region->start() < 0) {
4263                 /* Trim drag made start time -ve; fix this */
4264                 midi_region()->fix_negative_start ();
4265         }
4266 }
4267
4268 void
4269 MidiRegionView::edit_patch_change (PatchChange* pc)
4270 {
4271         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
4272
4273         int response = d.run();
4274
4275         switch (response) {
4276         case Gtk::RESPONSE_ACCEPT:
4277                 break;
4278         case Gtk::RESPONSE_REJECT:
4279                 delete_patch_change (pc);
4280                 return;
4281         default:
4282                 return;
4283         }
4284
4285         change_patch_change (pc->patch(), d.patch ());
4286 }
4287
4288 void
4289 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
4290 {
4291         // CAIROCANVAS
4292         // sysyex object doesn't have a pointer to a sysex event
4293         // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
4294         // c->remove (sysex->sysex());
4295         // _model->apply_command (*trackview.session(), c);
4296
4297         //_sys_exes.clear ();
4298         // display_sysexes();
4299 }
4300
4301 std::string
4302 MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
4303 {
4304         using namespace MIDI::Name;
4305         std::string name;
4306
4307         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4308         if (mtv) {
4309                 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
4310                 if (device_names) {
4311                         MIDI::Name::PatchPrimaryKey patch_key;
4312                         get_patch_key_at(n->time(), n->channel(), patch_key);
4313                         name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
4314                                                        n->channel(),
4315                                                        patch_key.bank(),
4316                                                        patch_key.program(),
4317                                                        note_value);
4318                 }
4319         }
4320
4321         char buf[128];
4322         snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
4323                   (int) note_value,
4324                   name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
4325                   (int) n->channel() + 1,
4326                   (int) n->velocity());
4327
4328         return buf;
4329 }
4330
4331 void
4332 MidiRegionView::show_verbose_cursor_for_new_note_value(boost::shared_ptr<NoteType> current_note,
4333                                                        uint8_t new_value) const
4334 {
4335         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4336         if (mtv) {
4337                 mtv->set_note_highlight (new_value);
4338         }
4339
4340         show_verbose_cursor(get_note_name(current_note, new_value), 10, 20);
4341 }
4342
4343 void
4344 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
4345 {
4346         show_verbose_cursor_for_new_note_value(n, n->note());
4347 }
4348
4349 void
4350 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
4351 {
4352         trackview.editor().verbose_cursor()->set (text);
4353         trackview.editor().verbose_cursor()->show ();
4354         trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
4355 }
4356
4357 uint8_t
4358 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
4359 {
4360         if (_model->notes().empty()) {
4361                 return 0x40;  // No notes, use default
4362         }
4363
4364         MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
4365         if (m == _model->notes().begin()) {
4366                 // Before the start, use the velocity of the first note
4367                 return (*m)->velocity();
4368         } else if (m == _model->notes().end()) {
4369                 // Past the end, use the velocity of the last note
4370                 --m;
4371                 return (*m)->velocity();
4372         }
4373
4374         // Interpolate velocity of surrounding notes
4375         MidiModel::Notes::const_iterator n = m;
4376         --n;
4377
4378         const double frac = ((time - (*n)->time()).to_double() /
4379                              ((*m)->time() - (*n)->time()).to_double());
4380
4381         return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4382 }
4383
4384 /** @param p A session samplepos.
4385  *  @param divisions beat division to snap given by Editor::get_grid_music_divisions() where
4386  *  bar is -1, 0 is audio samples and a positive integer is beat subdivisions.
4387  *  @return beat duration of p snapped to the grid subdivision underneath it.
4388  */
4389 Temporal::Beats
4390 MidiRegionView::snap_sample_to_grid_underneath (samplepos_t p, int32_t divisions, bool shift_snap) const
4391 {
4392         TempoMap& map (trackview.session()->tempo_map());
4393         double eqaf = map.exact_qn_at_sample (p + _region->position(), divisions);
4394
4395         if (divisions != 0 && shift_snap) {
4396                 const double qaf = map.quarter_note_at_sample (p + _region->position());
4397                 /* Hack so that we always snap to the note that we are over, instead of snapping
4398                    to the next one if we're more than halfway through the one we're over.
4399                 */
4400                 const Temporal::Beats grid_beats = get_grid_beats (p + _region->position());
4401                 const double rem = eqaf - qaf;
4402                 if (rem >= 0.0) {
4403                         eqaf -= grid_beats.to_double();
4404                 }
4405         }
4406         const double session_start_off = _region->quarter_note() - midi_region()->start_beats();
4407
4408         return Temporal::Beats (eqaf - session_start_off);
4409 }
4410
4411 ChannelMode
4412 MidiRegionView::get_channel_mode () const
4413 {
4414         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4415         return rtav->midi_track()->get_playback_channel_mode();
4416 }
4417
4418 uint16_t
4419 MidiRegionView::get_selected_channels () const
4420 {
4421         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4422         return rtav->midi_track()->get_playback_channel_mask();
4423 }
4424
4425
4426 Temporal::Beats
4427 MidiRegionView::get_grid_beats(samplepos_t pos) const
4428 {
4429         PublicEditor& editor  = trackview.editor();
4430         bool          success = false;
4431         Temporal::Beats beats   = editor.get_grid_type_as_beats (success, pos);
4432         if (!success) {
4433                 beats = Temporal::Beats(1);
4434         }
4435         return beats;
4436 }
4437 uint8_t
4438 MidiRegionView::y_to_note (double y) const
4439 {
4440         int const n = ((contents_height() - y) / contents_height() * (double)(_current_range_max - _current_range_min + 1))
4441                 + _current_range_min;
4442
4443         if (n < 0) {
4444                 return 0;
4445         } else if (n > 127) {
4446                 return 127;
4447         }
4448
4449         /* min due to rounding and/or off-by-one errors */
4450         return min ((uint8_t) n, _current_range_max);
4451 }
4452
4453 double
4454 MidiRegionView::note_to_y(uint8_t note) const
4455 {
4456         return contents_height() - (note + 1 - _current_range_min) * note_height() + 1;
4457 }
4458
4459 double
4460 MidiRegionView::session_relative_qn (double qn) const
4461 {
4462         return qn + (region()->quarter_note() - midi_region()->start_beats());
4463 }