4612b7fd97bd8c44481a654cf6e2b3460f912f70
[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 0) alone since
1198                            they are being extended. */
1199                         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1200                                 if (i->second->note()->length() > 0) {
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(
1639                         end_time - _active_notes[note]->note()->time());
1640
1641                 /* End time is relative to the region being recorded. */
1642                 const samplepos_t end_time_samples = region_beats_to_region_samples(end_time);
1643
1644                 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_samples));
1645                 _active_notes[note]->set_outline_all ();
1646                 _active_notes[note] = 0;
1647         }
1648 }
1649
1650
1651 /** Extend active notes to rightmost edge of region (if length is changed)
1652  */
1653 void
1654 MidiRegionView::extend_active_notes()
1655 {
1656         if (!_active_notes) {
1657                 return;
1658         }
1659
1660         for (unsigned i = 0; i < 128; ++i) {
1661                 if (_active_notes[i]) {
1662                         _active_notes[i]->set_x1(
1663                                 trackview.editor().sample_to_pixel(_region->length()));
1664                 }
1665         }
1666 }
1667
1668 void
1669 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1670 {
1671         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1672                 return;
1673         }
1674
1675         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1676
1677         if (!route_ui || !route_ui->midi_track()) {
1678                 return;
1679         }
1680
1681         NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1682         np->add (note);
1683         np->play ();
1684
1685         /* NotePlayer deletes itself */
1686 }
1687
1688 void
1689 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1690 {
1691         const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1692         start_playing_midi_chord(notes);
1693 }
1694
1695 void
1696 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1697 {
1698         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1699                 return;
1700         }
1701
1702         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1703
1704         if (!route_ui || !route_ui->midi_track()) {
1705                 return;
1706         }
1707
1708         NotePlayer* player = new NotePlayer (route_ui->midi_track());
1709
1710         for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1711                 player->add (*n);
1712         }
1713
1714         player->play ();
1715 }
1716
1717
1718 bool
1719 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1720 {
1721         const boost::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region();
1722
1723         /* must compare double explicitly as Beats::operator< rounds to ppqn */
1724         const bool outside = (note->time().to_double() < midi_reg->start_beats() ||
1725                               note->time().to_double() >= midi_reg->start_beats() + midi_reg->length_beats());
1726
1727         visible = (note->note() >= _current_range_min) &&
1728                 (note->note() <= _current_range_max);
1729
1730         return !outside;
1731 }
1732
1733 void
1734 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1735 {
1736         Note* sus = NULL;
1737         Hit*  hit = NULL;
1738         if ((sus = dynamic_cast<Note*>(note))) {
1739                 update_sustained(sus, update_ghost_regions);
1740         } else if ((hit = dynamic_cast<Hit*>(note))) {
1741                 update_hit(hit, update_ghost_regions);
1742         }
1743 }
1744
1745 /** Update a canvas note's size from its model note.
1746  *  @param ev Canvas note to update.
1747  *  @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1748  */
1749 void
1750 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1751 {
1752         TempoMap& map (trackview.session()->tempo_map());
1753         const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
1754         boost::shared_ptr<NoteType> note = ev->note();
1755
1756         const double session_source_start = _region->quarter_note() - mr->start_beats();
1757         const samplepos_t note_start_samples = map.sample_at_quarter_note (note->time().to_double() + session_source_start) - _region->position();
1758
1759         const double x0 = max (0.,trackview.editor().sample_to_pixel (note_start_samples));
1760         double x1;
1761         const double y0 = 1 + floor(note_to_y(note->note()));
1762         double y1;
1763
1764         if (note->length().to_double() > 0.0) {
1765                 double note_end_time = note->end_time().to_double();
1766
1767                 if (note_end_time > mr->start_beats() + mr->length_beats()) {
1768                         note_end_time = mr->start_beats() + mr->length_beats();
1769                 }
1770
1771                 const samplepos_t note_end_samples = map.sample_at_quarter_note (session_source_start + note_end_time) - _region->position();
1772
1773                 x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_samples)) - 1;
1774         } else {
1775                 x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
1776         }
1777
1778         y1 = y0 + std::max(1., floor(note_height()) - 1);
1779
1780         ev->set (ArdourCanvas::Rect (x0, y0, x1, y1));
1781         ev->set_velocity (note->velocity()/127.0);
1782
1783         if (!note->length()) {
1784                 if (_active_notes && note->note() < 128) {
1785                         Note* const old_rect = _active_notes[note->note()];
1786                         if (old_rect) {
1787                                 /* There is an active note on this key, so we have a stuck
1788                                    note.  Finish the old rectangle here. */
1789                                 old_rect->set_x1 (x1);
1790                                 old_rect->set_outline_all ();
1791                         }
1792                         _active_notes[note->note()] = ev;
1793                 }
1794                 /* outline all but right edge */
1795                 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1796                                               ArdourCanvas::Rectangle::TOP|
1797                                               ArdourCanvas::Rectangle::LEFT|
1798                                               ArdourCanvas::Rectangle::BOTTOM));
1799         } else {
1800                 /* outline all edges */
1801                 ev->set_outline_all ();
1802         }
1803
1804         // Update color in case velocity has changed
1805         const uint32_t base_col = ev->base_color();
1806         ev->set_fill_color(base_col);
1807         ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
1808
1809 }
1810
1811 void
1812 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1813 {
1814         boost::shared_ptr<NoteType> note = ev->note();
1815
1816         const double note_time_qn = note->time().to_double() + (_region->quarter_note() - midi_region()->start_beats());
1817         const samplepos_t note_start_samples = trackview.session()->tempo_map().sample_at_quarter_note (note_time_qn) - _region->position();
1818
1819         const double x = trackview.editor().sample_to_pixel(note_start_samples);
1820         const double diamond_size = std::max(1., floor(note_height()) - 2.);
1821         const double y = 1.5 + floor(note_to_y(note->note())) + diamond_size * .5;
1822
1823         // see DnD note in MidiRegionView::apply_note_range() above
1824         if (y <= 0 || y >= _height) {
1825                 ev->hide();
1826         } else {
1827                 ev->show();
1828         }
1829
1830         ev->set_position (ArdourCanvas::Duple (x, y));
1831         ev->set_height (diamond_size);
1832
1833         // Update color in case velocity has changed
1834         const uint32_t base_col = ev->base_color();
1835         ev->set_fill_color(base_col);
1836         ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
1837
1838 }
1839
1840 /** Add a MIDI note to the view (with length).
1841  *
1842  * If in sustained mode, notes with length 0 will be considered active
1843  * notes, and resolve_note should be called when the corresponding note off
1844  * event arrives, to properly display the note.
1845  */
1846 NoteBase*
1847 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1848 {
1849         NoteBase* event = 0;
1850
1851         if (midi_view()->note_mode() == Sustained) {
1852
1853                 Note* ev_rect = new Note (*this, _note_group, note); // XXX may leak
1854
1855                 update_sustained (ev_rect);
1856
1857                 event = ev_rect;
1858
1859         } else if (midi_view()->note_mode() == Percussive) {
1860
1861                 const double diamond_size = std::max(1., floor(note_height()) - 2.);
1862
1863                 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); // XXX may leak
1864
1865                 update_hit (ev_diamond);
1866
1867                 event = ev_diamond;
1868
1869         } else {
1870                 event = 0;
1871         }
1872
1873         if (event) {
1874                 MidiGhostRegion* gr;
1875
1876                 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1877                         if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1878                                 gr->add_note(event);
1879                         }
1880                 }
1881
1882                 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1883                         note_selected(event, true);
1884                 }
1885
1886                 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1887                         event->show_velocity();
1888                 }
1889
1890                 event->on_channel_selection_change (get_selected_channels());
1891                 _events.insert (make_pair (event->note(), event));
1892
1893                 if (visible) {
1894                         event->show();
1895                 } else {
1896                         event->hide ();
1897                 }
1898         }
1899
1900         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1901         MidiStreamView* const view = mtv->midi_view();
1902
1903         view->update_note_range (note->note());
1904         return event;
1905 }
1906
1907 void
1908 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1909                                Temporal::Beats pos, Temporal::Beats len)
1910 {
1911         boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1912
1913         /* potentially extend region to hold new note */
1914
1915         samplepos_t end_sample = source_beats_to_absolute_samples (new_note->end_time());
1916         samplepos_t region_end = _region->last_sample();
1917
1918         if (end_sample > region_end) {
1919                 /* XX sets length in beats from audio space. make musical */
1920                 _region->set_length (end_sample - _region->position(), 0);
1921         }
1922
1923         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1924         MidiStreamView* const view = mtv->midi_view();
1925
1926         view->update_note_range(new_note->note());
1927
1928         _marked_for_selection.clear ();
1929
1930         start_note_diff_command (_("step add"));
1931
1932         clear_editor_note_selection ();
1933         note_diff_add_note (new_note, true, false);
1934
1935         apply_diff();
1936
1937         // last_step_edit_note = new_note;
1938 }
1939
1940 void
1941 MidiRegionView::step_sustain (Temporal::Beats beats)
1942 {
1943         change_note_lengths (false, false, beats, false, true);
1944 }
1945
1946 /** Add a new patch change flag to the canvas.
1947  * @param patch the patch change to add
1948  * @param the text to display in the flag
1949  * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1950  */
1951 void
1952 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1953 {
1954         samplecnt_t region_samples = source_beats_to_region_samples (patch->time());
1955         const double x = trackview.editor().sample_to_pixel (region_samples);
1956
1957         double const height = midi_stream_view()->contents_height();
1958
1959         // CAIROCANVAS: active_channel info removed from PatcChange constructor
1960         // so we need to do something more sophisticated to keep its color
1961         // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1962         // up to date.
1963         boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1964                 new PatchChange(*this, group,
1965                                 displaytext,
1966                                 height,
1967                                 x, 1.0,
1968                                 instrument_info(),
1969                                 patch,
1970                                 _patch_change_outline,
1971                                 _patch_change_fill)
1972                 );
1973
1974         if (patch_change->item().width() < _pixel_width) {
1975                 // Show unless patch change is beyond the region bounds
1976                 if (region_samples < 0 || region_samples >= _region->length()) {
1977                         patch_change->hide();
1978                 } else {
1979                         patch_change->show();
1980                 }
1981         } else {
1982                 patch_change->hide ();
1983         }
1984
1985         _patch_changes.insert (make_pair (patch, patch_change));
1986 }
1987
1988 void
1989 MidiRegionView::remove_canvas_patch_change (PatchChange* pc)
1990 {
1991         /* remove the canvas item */
1992         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1993                 if (x->second->patch() == pc->patch()) {
1994                         _patch_changes.erase (x);
1995                         break;
1996                 }
1997         }
1998 }
1999
2000 MIDI::Name::PatchPrimaryKey
2001 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
2002 {
2003         return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
2004 }
2005
2006 /// Return true iff @p pc applies to the given time on the given channel.
2007 static bool
2008 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Temporal::Beats time, uint8_t channel)
2009 {
2010         return pc->time() <= time && pc->channel() == channel;
2011 }
2012
2013 void
2014 MidiRegionView::get_patch_key_at (Temporal::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
2015 {
2016         // The earliest event not before time
2017         MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
2018
2019         // Go backwards until we find the latest PC for this channel, or the start
2020         while (i != _model->patch_changes().begin() &&
2021                (i == _model->patch_changes().end() ||
2022                 !patch_applies(*i, time, channel))) {
2023                 --i;
2024         }
2025
2026         if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
2027                 key.set_bank((*i)->bank());
2028                 key.set_program((*i)->program ());
2029         } else {
2030                 key.set_bank(0);
2031                 key.set_program(0);
2032         }
2033 }
2034
2035 void
2036 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
2037 {
2038         string name = _("alter patch change");
2039         trackview.editor().begin_reversible_command (name);
2040
2041         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2042
2043         if (pc.patch()->program() != new_patch.program()) {
2044                 c->change_program (pc.patch (), new_patch.program());
2045         }
2046
2047         int const new_bank = new_patch.bank();
2048         if (pc.patch()->bank() != new_bank) {
2049                 c->change_bank (pc.patch (), new_bank);
2050         }
2051
2052         _model->apply_command (*trackview.session(), c);
2053         trackview.editor().commit_reversible_command ();
2054
2055         remove_canvas_patch_change (&pc);
2056         display_patch_changes ();
2057 }
2058
2059 void
2060 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Temporal::Beats> & new_change)
2061 {
2062         string name = _("alter patch change");
2063         trackview.editor().begin_reversible_command (name);
2064         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2065
2066         if (old_change->time() != new_change.time()) {
2067                 c->change_time (old_change, new_change.time());
2068         }
2069
2070         if (old_change->channel() != new_change.channel()) {
2071                 c->change_channel (old_change, new_change.channel());
2072         }
2073
2074         if (old_change->program() != new_change.program()) {
2075                 c->change_program (old_change, new_change.program());
2076         }
2077
2078         if (old_change->bank() != new_change.bank()) {
2079                 c->change_bank (old_change, new_change.bank());
2080         }
2081
2082         _model->apply_command (*trackview.session(), c);
2083         trackview.editor().commit_reversible_command ();
2084
2085         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
2086                 if (x->second->patch() == old_change) {
2087                         _patch_changes.erase (x);
2088                         break;
2089                 }
2090         }
2091
2092         display_patch_changes ();
2093 }
2094
2095 /** Add a patch change to the region.
2096  *  @param t Time in samples relative to region position
2097  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2098  *  MidiTimeAxisView::get_channel_for_add())
2099  */
2100 void
2101 MidiRegionView::add_patch_change (samplecnt_t t, Evoral::PatchChange<Temporal::Beats> const & patch)
2102 {
2103         string name = _("add patch change");
2104
2105         trackview.editor().begin_reversible_command (name);
2106         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2107         c->add (MidiModel::PatchChangePtr (
2108                         new Evoral::PatchChange<Temporal::Beats> (
2109                                 absolute_samples_to_source_beats (_region->position() + t),
2110                                 patch.channel(), patch.program(), patch.bank()
2111                                 )
2112                         )
2113                 );
2114
2115         _model->apply_command (*trackview.session(), c);
2116         trackview.editor().commit_reversible_command ();
2117
2118         display_patch_changes ();
2119 }
2120
2121 void
2122 MidiRegionView::move_patch_change (PatchChange& pc, Temporal::Beats t)
2123 {
2124         trackview.editor().begin_reversible_command (_("move patch change"));
2125         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2126         c->change_time (pc.patch (), t);
2127         _model->apply_command (*trackview.session(), c);
2128         trackview.editor().commit_reversible_command ();
2129
2130         display_patch_changes ();
2131 }
2132
2133 void
2134 MidiRegionView::delete_patch_change (PatchChange* pc)
2135 {
2136         trackview.editor().begin_reversible_command (_("delete patch change"));
2137
2138         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2139         c->remove (pc->patch ());
2140         _model->apply_command (*trackview.session(), c);
2141         trackview.editor().commit_reversible_command ();
2142
2143         remove_canvas_patch_change (pc);
2144         display_patch_changes ();
2145 }
2146
2147 void
2148 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2149 {
2150         MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2151         if (bank) {
2152                 key.set_bank(key.bank() + delta);
2153         } else {
2154                 key.set_program(key.program() + delta);
2155         }
2156         change_patch_change(patch, key);
2157 }
2158
2159 void
2160 MidiRegionView::note_deleted (NoteBase* cne)
2161 {
2162         if (_entered_note && cne == _entered_note) {
2163                 _entered_note = 0;
2164         }
2165
2166         if (_selection.empty()) {
2167                 return;
2168         }
2169
2170         _selection.erase (cne);
2171 }
2172
2173 void
2174 MidiRegionView::delete_selection()
2175 {
2176         if (_selection.empty()) {
2177                 return;
2178         }
2179
2180         if (trackview.editor().drags()->active()) {
2181                 return;
2182         }
2183
2184         start_note_diff_command (_("delete selection"));
2185
2186         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2187                 if ((*i)->selected()) {
2188                         _note_diff_command->remove((*i)->note());
2189                 }
2190         }
2191
2192         _selection.clear();
2193
2194         apply_diff ();
2195
2196         hide_verbose_cursor ();
2197 }
2198
2199 void
2200 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2201 {
2202         start_note_diff_command (_("delete note"));
2203         _note_diff_command->remove (n);
2204         apply_diff ();
2205
2206         hide_verbose_cursor ();
2207 }
2208
2209 void
2210 MidiRegionView::clear_editor_note_selection ()
2211 {
2212         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_editor_note_selection\n");
2213         PublicEditor& editor(trackview.editor());
2214         editor.get_selection().clear_midi_notes();
2215 }
2216
2217 void
2218 MidiRegionView::clear_selection ()
2219 {
2220         clear_selection_internal();
2221         PublicEditor& editor(trackview.editor());
2222         editor.get_selection().remove(this);
2223 }
2224
2225 void
2226 MidiRegionView::clear_selection_internal ()
2227 {
2228         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_selection_internal\n");
2229
2230         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2231                 (*i)->set_selected(false);
2232                 (*i)->hide_velocity();
2233         }
2234         _selection.clear();
2235
2236         if (_entered) {
2237                 // Clearing selection entirely, ungrab keyboard
2238                 Keyboard::magic_widget_drop_focus();
2239                 _grabbed_keyboard = false;
2240         }
2241 }
2242
2243 void
2244 MidiRegionView::unique_select(NoteBase* ev)
2245 {
2246         clear_editor_note_selection();
2247         add_to_selection(ev);
2248 }
2249
2250 void
2251 MidiRegionView::select_all_notes ()
2252 {
2253         clear_editor_note_selection ();
2254
2255         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2256                 add_to_selection (i->second);
2257         }
2258 }
2259
2260 void
2261 MidiRegionView::select_range (samplepos_t start, samplepos_t end)
2262 {
2263         clear_editor_note_selection ();
2264
2265         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2266                 samplepos_t t = source_beats_to_absolute_samples(i->first->time());
2267                 if (t >= start && t <= end) {
2268                         add_to_selection (i->second);
2269                 }
2270         }
2271 }
2272
2273 void
2274 MidiRegionView::invert_selection ()
2275 {
2276         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2277                 if (i->second->selected()) {
2278                         remove_from_selection(i->second);
2279                 } else {
2280                         add_to_selection (i->second);
2281                 }
2282         }
2283 }
2284
2285 /** Used for selection undo/redo.
2286     The requested notes most likely won't exist in the view until the next model redisplay.
2287 */
2288 void
2289 MidiRegionView::select_notes (list<Evoral::event_id_t> notes)
2290 {
2291         NoteBase* cne;
2292         list<Evoral::event_id_t>::iterator n;
2293
2294         for (n = notes.begin(); n != notes.end(); ++n) {
2295                 if ((cne = find_canvas_note(*n)) != 0) {
2296                         add_to_selection (cne);
2297                 } else {
2298                         _pending_note_selection.insert(*n);
2299                 }
2300         }
2301 }
2302
2303 void
2304 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2305 {
2306         bool have_selection = !_selection.empty();
2307         uint8_t low_note = 127;
2308         uint8_t high_note = 0;
2309         MidiModel::Notes& notes (_model->notes());
2310         _optimization_iterator = _events.begin();
2311
2312         if (extend && !have_selection) {
2313                 extend = false;
2314         }
2315
2316         /* scan existing selection to get note range */
2317
2318         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2319                 if ((*i)->note()->note() < low_note) {
2320                         low_note = (*i)->note()->note();
2321                 }
2322                 if ((*i)->note()->note() > high_note) {
2323                         high_note = (*i)->note()->note();
2324                 }
2325         }
2326
2327         if (!add) {
2328                 clear_editor_note_selection ();
2329
2330                 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2331                         /* only note previously selected is the one we are
2332                          * reselecting. treat this as cancelling the selection.
2333                          */
2334                         return;
2335                 }
2336         }
2337
2338         if (extend) {
2339                 low_note = min (low_note, notenum);
2340                 high_note = max (high_note, notenum);
2341         }
2342
2343         _no_sound_notes = true;
2344
2345         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2346
2347                 boost::shared_ptr<NoteType> note (*n);
2348                 NoteBase* cne;
2349                 bool select = false;
2350
2351                 if (((1 << note->channel()) & channel_mask) != 0) {
2352                         if (extend) {
2353                                 if ((note->note() >= low_note && note->note() <= high_note)) {
2354                                         select = true;
2355                                 }
2356                         } else if (note->note() == notenum) {
2357                                 select = true;
2358                         }
2359                 }
2360
2361                 if (select) {
2362                         if ((cne = find_canvas_note (note)) != 0) {
2363                                 // extend is false because we've taken care of it,
2364                                 // since it extends by time range, not pitch.
2365                                 note_selected (cne, add, false);
2366                         }
2367                 }
2368
2369                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2370
2371         }
2372
2373         _no_sound_notes = false;
2374 }
2375
2376 void
2377 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2378 {
2379         MidiModel::Notes& notes (_model->notes());
2380         _optimization_iterator = _events.begin();
2381
2382         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2383
2384                 boost::shared_ptr<NoteType> note (*n);
2385                 NoteBase* cne;
2386
2387                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2388                         if ((cne = find_canvas_note (note)) != 0) {
2389                                 if (cne->selected()) {
2390                                         note_deselected (cne);
2391                                 } else {
2392                                         note_selected (cne, true, false);
2393                                 }
2394                         }
2395                 }
2396         }
2397 }
2398
2399 void
2400 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2401 {
2402         if (!add) {
2403                 clear_editor_note_selection();
2404                 add_to_selection (ev);
2405         }
2406
2407         if (!extend) {
2408
2409                 if (!ev->selected()) {
2410                         add_to_selection (ev);
2411                 }
2412
2413         } else {
2414                 /* find end of latest note selected, select all between that and the start of "ev" */
2415
2416                 Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
2417                 Temporal::Beats latest   = Temporal::Beats();
2418
2419                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2420                         if ((*i)->note()->end_time() > latest) {
2421                                 latest = (*i)->note()->end_time();
2422                         }
2423                         if ((*i)->note()->time() < earliest) {
2424                                 earliest = (*i)->note()->time();
2425                         }
2426                 }
2427
2428                 if (ev->note()->end_time() > latest) {
2429                         latest = ev->note()->end_time();
2430                 }
2431
2432                 if (ev->note()->time() < earliest) {
2433                         earliest = ev->note()->time();
2434                 }
2435
2436                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2437
2438                         /* find notes entirely within OR spanning the earliest..latest range */
2439
2440                         if ((i->first->time() >= earliest && i->first->end_time() <= latest) ||
2441                             (i->first->time() <= earliest && i->first->end_time() >= latest)) {
2442                                 add_to_selection (i->second);
2443                         }
2444                 }
2445         }
2446 }
2447
2448 void
2449 MidiRegionView::note_deselected(NoteBase* ev)
2450 {
2451         remove_from_selection (ev);
2452 }
2453
2454 void
2455 MidiRegionView::update_drag_selection(samplepos_t start, samplepos_t end, double gy0, double gy1, bool extend)
2456 {
2457         PublicEditor& editor = trackview.editor();
2458
2459         // Convert to local coordinates
2460         const samplepos_t p  = _region->position();
2461         const double     y  = midi_view()->y_position();
2462         const double     x0 = editor.sample_to_pixel(max((samplepos_t)0, start - p));
2463         const double     x1 = editor.sample_to_pixel(max((samplepos_t)0, end - p));
2464         const double     y0 = max(0.0, gy0 - y);
2465         const double     y1 = max(0.0, gy1 - y);
2466
2467         // TODO: Make this faster by storing the last updated selection rect, and only
2468         // adjusting things that are in the area that appears/disappeared.
2469         // We probably need a tree to be able to find events in O(log(n)) time.
2470
2471         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2472                 if (i->second->x0() < x1 && i->second->x1() > x0 && i->second->y0() < y1 && i->second->y1() > y0) {
2473                         // Rectangles intersect
2474                         if (!i->second->selected()) {
2475                                 add_to_selection (i->second);
2476                         }
2477                 } else if (i->second->selected() && !extend) {
2478                         // Rectangles do not intersect
2479                         remove_from_selection (i->second);
2480                 }
2481         }
2482
2483         typedef RouteTimeAxisView::AutomationTracks ATracks;
2484         typedef std::list<Selectable*>              Selectables;
2485
2486         /* Add control points to selection. */
2487         const ATracks& atracks = midi_view()->automation_tracks();
2488         Selectables    selectables;
2489         editor.get_selection().clear_points();
2490         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2491                 a->second->get_selectables(start, end, gy0, gy1, selectables);
2492                 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2493                         ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2494                         if (cp) {
2495                                 editor.get_selection().add(cp);
2496                         }
2497                 }
2498                 a->second->set_selected_points(editor.get_selection().points);
2499         }
2500 }
2501
2502 void
2503 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2504 {
2505         if (y1 > y2) {
2506                 swap (y1, y2);
2507         }
2508
2509         // TODO: Make this faster by storing the last updated selection rect, and only
2510         // adjusting things that are in the area that appears/disappeared.
2511         // We probably need a tree to be able to find events in O(log(n)) time.
2512
2513         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2514                 if ((i->second->y1() >= y1 && i->second->y1() <= y2)) {
2515                         // within y- (note-) range
2516                         if (!i->second->selected()) {
2517                                 add_to_selection (i->second);
2518                         }
2519                 } else if (i->second->selected() && !extend) {
2520                         remove_from_selection (i->second);
2521                 }
2522         }
2523 }
2524
2525 void
2526 MidiRegionView::remove_from_selection (NoteBase* ev)
2527 {
2528         Selection::iterator i = _selection.find (ev);
2529
2530         if (i != _selection.end()) {
2531                 _selection.erase (i);
2532                 if (_selection.empty() && _grabbed_keyboard) {
2533                         // Ungrab keyboard
2534                         Keyboard::magic_widget_drop_focus();
2535                         _grabbed_keyboard = false;
2536                 }
2537         }
2538
2539         ev->set_selected (false);
2540         ev->hide_velocity ();
2541
2542         if (_selection.empty()) {
2543                 PublicEditor& editor (trackview.editor());
2544                 editor.get_selection().remove (this);
2545         }
2546 }
2547
2548 void
2549 MidiRegionView::add_to_selection (NoteBase* ev)
2550 {
2551         const bool selection_was_empty = _selection.empty();
2552
2553         if (_selection.insert (ev).second) {
2554                 ev->set_selected (true);
2555                 start_playing_midi_note ((ev)->note());
2556                 if (selection_was_empty && _entered) {
2557                         // Grab keyboard for moving notes with arrow keys
2558                         Keyboard::magic_widget_grab_focus();
2559                         _grabbed_keyboard = true;
2560                 }
2561         }
2562
2563         if (selection_was_empty) {
2564                 PublicEditor& editor (trackview.editor());
2565                 editor.get_selection().add (this);
2566         }
2567 }
2568
2569 Temporal::Beats
2570 MidiRegionView::earliest_in_selection ()
2571 {
2572         Temporal::Beats earliest = std::numeric_limits<Temporal::Beats>::max();
2573
2574         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2575                 if ((*i)->note()->time() < earliest) {
2576                         earliest = (*i)->note()->time();
2577                 }
2578         }
2579
2580         return earliest;
2581 }
2582
2583 void
2584 MidiRegionView::move_selection(double dx_qn, double dy, double cumulative_dy)
2585 {
2586         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2587         Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
2588         TempoMap& tmap (editor->session()->tempo_map());
2589         PossibleChord to_play;
2590         Temporal::Beats earliest = earliest_in_selection();
2591
2592         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2593                 NoteBase* n = *i;
2594                 if (n->note()->time() == earliest) {
2595                         to_play.push_back (n->note());
2596                 }
2597                 double const note_time_qn = session_relative_qn (n->note()->time().to_double());
2598                 double dx = 0.0;
2599                 if (midi_view()->note_mode() == Sustained) {
2600                         dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2601                                 - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
2602                 } else {
2603                         /* Hit::x0() is offset by _position.x, unlike Note::x0() */
2604                         Hit* hit = dynamic_cast<Hit*>(n);
2605                         if (hit) {
2606                                 dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2607                                         - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
2608                         }
2609                 }
2610
2611                 (*i)->move_event(dx, dy);
2612
2613                 /* update length */
2614                 if (midi_view()->note_mode() == Sustained) {
2615                         Note* sus = dynamic_cast<Note*> (*i);
2616                         double const len_dx = editor->sample_to_pixel_unrounded (
2617                                 tmap.sample_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
2618
2619                         sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
2620                 }
2621         }
2622
2623         if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2624
2625                 if (to_play.size() > 1) {
2626
2627                         PossibleChord shifted;
2628
2629                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2630                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2631                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2632                                 shifted.push_back (moved_note);
2633                         }
2634
2635                         start_playing_midi_chord (shifted);
2636
2637                 } else if (!to_play.empty()) {
2638
2639                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2640                         moved_note->set_note (moved_note->note() + cumulative_dy);
2641                         start_playing_midi_note (moved_note);
2642                 }
2643         }
2644 }
2645
2646 NoteBase*
2647 MidiRegionView::copy_selection (NoteBase* primary)
2648 {
2649         _copy_drag_events.clear ();
2650
2651         if (_selection.empty()) {
2652                 return 0;
2653         }
2654
2655         NoteBase* note;
2656         NoteBase* ret = 0;
2657
2658         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2659                 boost::shared_ptr<NoteType> g (new NoteType (*((*i)->note())));
2660                 if (midi_view()->note_mode() == Sustained) {
2661                         Note* n = new Note (*this, _note_group, g);
2662                         update_sustained (n, false);
2663                         note = n;
2664                 } else {
2665                         Hit* h = new Hit (*this, _note_group, 10, g);
2666                         update_hit (h, false);
2667                         note = h;
2668                 }
2669
2670                 if ((*i) == primary) {
2671                         ret = note;
2672                 }
2673
2674                 _copy_drag_events.push_back (note);
2675         }
2676
2677         return ret;
2678 }
2679
2680 void
2681 MidiRegionView::move_copies (double dx_qn, double dy, double cumulative_dy)
2682 {
2683         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2684         Editor* editor = dynamic_cast<Editor*> (&trackview.editor());
2685         TempoMap& tmap (editor->session()->tempo_map());
2686         PossibleChord to_play;
2687         Temporal::Beats earliest = earliest_in_selection();
2688
2689         for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2690                 NoteBase* n = *i;
2691                 if (n->note()->time() == earliest) {
2692                         to_play.push_back (n->note());
2693                 }
2694                 double const note_time_qn = session_relative_qn (n->note()->time().to_double());
2695                 double dx = 0.0;
2696                 if (midi_view()->note_mode() == Sustained) {
2697                         dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2698                                 - n->item()->item_to_canvas (ArdourCanvas::Duple (n->x0(), 0)).x;
2699                 } else {
2700                         Hit* hit = dynamic_cast<Hit*>(n);
2701                         if (hit) {
2702                                 dx = editor->sample_to_pixel_unrounded (tmap.sample_at_quarter_note (note_time_qn + dx_qn))
2703                                         - n->item()->item_to_canvas (ArdourCanvas::Duple (((hit->x0() + hit->x1()) / 2.0) - hit->position().x, 0)).x;
2704                         }
2705                 }
2706
2707                 (*i)->move_event(dx, dy);
2708
2709                 if (midi_view()->note_mode() == Sustained) {
2710                         Note* sus = dynamic_cast<Note*> (*i);
2711                         double const len_dx = editor->sample_to_pixel_unrounded (
2712                                 tmap.sample_at_quarter_note (note_time_qn + dx_qn + n->note()->length().to_double()));
2713
2714                         sus->set_x1 (n->item()->canvas_to_item (ArdourCanvas::Duple (len_dx, 0)).x);
2715                 }
2716         }
2717
2718         if (dy && !_copy_drag_events.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2719
2720                 if (to_play.size() > 1) {
2721
2722                         PossibleChord shifted;
2723
2724                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2725                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2726                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2727                                 shifted.push_back (moved_note);
2728                         }
2729
2730                         start_playing_midi_chord (shifted);
2731
2732                 } else if (!to_play.empty()) {
2733
2734                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2735                         moved_note->set_note (moved_note->note() + cumulative_dy);
2736                         start_playing_midi_note (moved_note);
2737                 }
2738         }
2739 }
2740
2741 void
2742 MidiRegionView::note_dropped(NoteBase *, double d_qn, int8_t dnote, bool copy)
2743 {
2744         uint8_t lowest_note_in_selection  = 127;
2745         uint8_t highest_note_in_selection = 0;
2746         uint8_t highest_note_difference   = 0;
2747
2748         if (!copy) {
2749                 // find highest and lowest notes first
2750
2751                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2752                         uint8_t pitch = (*i)->note()->note();
2753                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2754                         highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2755                 }
2756
2757                 /*
2758                   cerr << "dnote: " << (int) dnote << endl;
2759                   cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2760                   << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2761                   cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2762                   << int(highest_note_in_selection) << endl;
2763                   cerr << "selection size: " << _selection.size() << endl;
2764                   cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2765                 */
2766
2767                 // Make sure the note pitch does not exceed the MIDI standard range
2768                 if (highest_note_in_selection + dnote > 127) {
2769                         highest_note_difference = highest_note_in_selection - 127;
2770                 }
2771
2772                 start_note_diff_command (_("move notes"));
2773
2774                 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2775
2776                         Temporal::Beats new_time = Temporal::Beats ((*i)->note()->time().to_double() + d_qn);
2777
2778                         if (new_time < 0) {
2779                                 continue;
2780                         }
2781
2782                         note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2783
2784                         uint8_t original_pitch = (*i)->note()->note();
2785                         uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2786
2787                         // keep notes in standard midi range
2788                         clamp_to_0_127(new_pitch);
2789
2790                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2791                         highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2792
2793                         note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2794                 }
2795         } else {
2796
2797                 clear_editor_note_selection ();
2798
2799                 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2800                         uint8_t pitch = (*i)->note()->note();
2801                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2802                         highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2803                 }
2804
2805                 // Make sure the note pitch does not exceed the MIDI standard range
2806                 if (highest_note_in_selection + dnote > 127) {
2807                         highest_note_difference = highest_note_in_selection - 127;
2808                 }
2809
2810                 start_note_diff_command (_("copy notes"));
2811
2812                 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) {
2813
2814                         /* update time */
2815                         Temporal::Beats new_time = Temporal::Beats ((*i)->note()->time().to_double() + d_qn);
2816
2817                         if (new_time < 0) {
2818                                 continue;
2819                         }
2820
2821                         (*i)->note()->set_time (new_time);
2822
2823                         /* update pitch */
2824
2825                         uint8_t original_pitch = (*i)->note()->note();
2826                         uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2827
2828                         (*i)->note()->set_note (new_pitch);
2829
2830                         // keep notes in standard midi range
2831                         clamp_to_0_127(new_pitch);
2832
2833                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2834                         highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2835
2836                         note_diff_add_note ((*i)->note(), true);
2837
2838                         delete *i;
2839                 }
2840
2841                 _copy_drag_events.clear ();
2842         }
2843
2844         apply_diff (false, copy);
2845
2846         // care about notes being moved beyond the upper/lower bounds on the canvas
2847         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
2848             highest_note_in_selection > midi_stream_view()->highest_note()) {
2849                 midi_stream_view()->set_note_range (MidiStreamView::ContentsRange);
2850         }
2851 }
2852
2853 /** @param x Pixel relative to the region position.
2854  *  @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
2855  *  Used for inverting the snap logic with key modifiers and snap delta calculation.
2856  *  @return Snapped sample relative to the region position.
2857  */
2858 samplepos_t
2859 MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
2860 {
2861         PublicEditor& editor (trackview.editor());
2862         return snap_sample_to_sample (editor.pixel_to_sample (x), ensure_snap).sample;
2863 }
2864
2865 /** @param x Pixel relative to the region position.
2866  *  @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
2867  *  @return Snapped pixel relative to the region position.
2868  */
2869 double
2870 MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
2871 {
2872         return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
2873 }
2874
2875 double
2876 MidiRegionView::get_position_pixels()
2877 {
2878         samplepos_t region_sample = get_position();
2879         return trackview.editor().sample_to_pixel(region_sample);
2880 }
2881
2882 double
2883 MidiRegionView::get_end_position_pixels()
2884 {
2885         samplepos_t sample = get_position() + get_duration ();
2886         return trackview.editor().sample_to_pixel(sample);
2887 }
2888
2889 samplepos_t
2890 MidiRegionView::source_beats_to_absolute_samples(Temporal::Beats beats) const
2891 {
2892         /* the time converter will return the sample corresponding to `beats'
2893            relative to the start of the source. The start of the source
2894            is an implied position given by region->position - region->start
2895         */
2896         const samplepos_t source_start = _region->position() - _region->start();
2897         return  source_start +  _source_relative_time_converter.to (beats);
2898 }
2899
2900 Temporal::Beats
2901 MidiRegionView::absolute_samples_to_source_beats(samplepos_t samples) const
2902 {
2903         /* the `samples' argument needs to be converted into a sample count
2904            relative to the start of the source before being passed in to the
2905            converter.
2906         */
2907         const samplepos_t source_start = _region->position() - _region->start();
2908         return  _source_relative_time_converter.from (samples - source_start);
2909 }
2910
2911 samplepos_t
2912 MidiRegionView::region_beats_to_region_samples(Temporal::Beats beats) const
2913 {
2914         return _region_relative_time_converter.to(beats);
2915 }
2916
2917 Temporal::Beats
2918 MidiRegionView::region_samples_to_region_beats(samplepos_t samples) const
2919 {
2920         return _region_relative_time_converter.from(samples);
2921 }
2922
2923 double
2924 MidiRegionView::region_samples_to_region_beats_double (samplepos_t samples) const
2925 {
2926         return _region_relative_time_converter_double.from(samples);
2927 }
2928
2929 void
2930 MidiRegionView::begin_resizing (bool /*at_front*/)
2931 {
2932         _resize_data.clear();
2933
2934         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2935                 Note *note = dynamic_cast<Note*> (*i);
2936
2937                 // only insert CanvasNotes into the map
2938                 if (note) {
2939                         NoteResizeData *resize_data = new NoteResizeData();
2940                         resize_data->note = note;
2941
2942                         // create a new SimpleRect from the note which will be the resize preview
2943                         ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2944                                                                                             ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2945
2946                         // calculate the colors: get the color settings
2947                         uint32_t fill_color = NoteBase::meter_style_fill_color (note->note()->velocity(), true);
2948
2949                         // make the resize preview notes more transparent and bright
2950                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2951
2952                         // calculate color based on note velocity
2953                         resize_rect->set_fill_color (UINT_INTERPOLATE(
2954                                 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2955                                 fill_color,
2956                                 0.85));
2957
2958                         resize_rect->set_outline_color (NoteBase::calculate_outline (
2959                                                                 UIConfiguration::instance().color ("midi note selected outline")));
2960
2961                         resize_data->resize_rect = resize_rect;
2962                         _resize_data.push_back(resize_data);
2963                 }
2964         }
2965 }
2966
2967 /** Update resizing notes while user drags.
2968  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2969  * @param at_front which end of the note (true == note on, false == note off)
2970  * @param delta_x change in mouse position since the start of the drag
2971  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2972  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2973  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2974  * as the \a primary note.
2975  * @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
2976  * @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
2977  */
2978 void
2979 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2980 {
2981         TempoMap& tmap (trackview.session()->tempo_map());
2982         bool cursor_set = false;
2983         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
2984
2985         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2986                 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2987                 Note* canvas_note = (*i)->note;
2988                 double current_x;
2989
2990                 if (at_front) {
2991                         if (relative) {
2992                                 current_x = canvas_note->x0() + delta_x + snap_delta;
2993                         } else {
2994                                 current_x = primary->x0() + delta_x + snap_delta;
2995                         }
2996                 } else {
2997                         if (relative) {
2998                                 current_x = canvas_note->x1() + delta_x + snap_delta;
2999                         } else {
3000                                 current_x = primary->x1() + delta_x + snap_delta;
3001                         }
3002                 }
3003
3004                 if (current_x < 0) {
3005                         /* This works even with snapping because RegionView::snap_sample_to_sample()
3006                          * snaps forward if the snapped sample is before the beginning of the region
3007                          */
3008                         current_x = 0;
3009                 }
3010                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
3011                         current_x = trackview.editor().sample_to_pixel(_region->length());
3012                 }
3013
3014                 if (at_front) {
3015                         if (with_snap) {
3016                                 resize_rect->set_x0 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
3017                         } else {
3018                                 resize_rect->set_x0 (current_x - snap_delta);
3019                         }
3020                         resize_rect->set_x1 (canvas_note->x1());
3021                 } else {
3022                         if (with_snap) {
3023                                 resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
3024                         } else {
3025                                 resize_rect->set_x1 (current_x - snap_delta);
3026                         }
3027                         resize_rect->set_x0 (canvas_note->x0());
3028                 }
3029
3030                 if (!cursor_set) {
3031                         /* Convert snap delta from pixels to beats. */
3032                         samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
3033                         double snap_delta_beats = 0.0;
3034                         int sign = 1;
3035
3036                         /* negative beat offsets aren't allowed */
3037                         if (snap_delta_samps > 0) {
3038                                 snap_delta_beats = region_samples_to_region_beats_double (snap_delta_samps);
3039                         } else if (snap_delta_samps < 0) {
3040                                 snap_delta_beats = region_samples_to_region_beats_double (- snap_delta_samps);
3041                                 sign = -1;
3042                         }
3043
3044                         double  snapped_x;
3045                         int32_t divisions = 0;
3046
3047                         if (with_snap) {
3048                                 snapped_x = snap_pixel_to_sample (current_x, ensure_snap);
3049                                 divisions = trackview.editor().get_grid_music_divisions (0);
3050                         } else {
3051                                 snapped_x = trackview.editor ().pixel_to_sample (current_x);
3052                         }
3053
3054                         const Temporal::Beats beats = Temporal::Beats (tmap.exact_beat_at_sample (snapped_x + midi_region()->position(), divisions)
3055                                                                        - midi_region()->beat())
3056                                                       + midi_region()->start_beats();
3057
3058                         Temporal::Beats len         = Temporal::Beats();
3059
3060                         if (at_front) {
3061                                 if (beats < canvas_note->note()->end_time()) {
3062                                         len = canvas_note->note()->time() - beats + (sign * snap_delta_beats);
3063                                         len += canvas_note->note()->length();
3064                                 }
3065                         } else {
3066                                 if (beats >= canvas_note->note()->time()) {
3067                                         len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
3068                                 }
3069                         }
3070
3071                         len = std::max(Temporal::Beats(1 / 512.0), len);
3072
3073                         char buf[16];
3074                         snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
3075                         show_verbose_cursor (buf, 0, 0);
3076
3077                         cursor_set = true;
3078
3079                         trackview.editor().set_snapped_cursor_position ( snapped_x + midi_region()->position() );
3080                 }
3081
3082         }
3083 }
3084
3085
3086 /** Finish resizing notes when the user releases the mouse button.
3087  *  Parameters the same as for \a update_resizing().
3088  */
3089 void
3090 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
3091 {
3092         _note_diff_command = _model->new_note_diff_command (_("resize notes"));
3093         TempoMap& tmap (trackview.session()->tempo_map());
3094
3095         /* XX why doesn't snap_pixel_to_sample() handle this properly? */
3096         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
3097
3098         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
3099                 Note*  canvas_note = (*i)->note;
3100                 ArdourCanvas::Rectangle*  resize_rect = (*i)->resize_rect;
3101
3102                 /* Get the new x position for this resize, which is in pixels relative
3103                  * to the region position.
3104                  */
3105
3106                 double current_x;
3107
3108                 if (at_front) {
3109                         if (relative) {
3110                                 current_x = canvas_note->x0() + delta_x + snap_delta;
3111                         } else {
3112                                 current_x = primary->x0() + delta_x + snap_delta;
3113                         }
3114                 } else {
3115                         if (relative) {
3116                                 current_x = canvas_note->x1() + delta_x + snap_delta;
3117                         } else {
3118                                 current_x = primary->x1() + delta_x + snap_delta;
3119                         }
3120                 }
3121
3122                 if (current_x < 0) {
3123                         current_x = 0;
3124                 }
3125                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
3126                         current_x = trackview.editor().sample_to_pixel(_region->length());
3127                 }
3128
3129                 /* Convert snap delta from pixels to beats with sign. */
3130                 samplepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
3131                 double snap_delta_beats = 0.0;
3132                 int sign = 1;
3133
3134                 if (snap_delta_samps > 0) {
3135                         snap_delta_beats = region_samples_to_region_beats_double (snap_delta_samps);
3136                 } else if (snap_delta_samps < 0) {
3137                         snap_delta_beats = region_samples_to_region_beats_double ( - snap_delta_samps);
3138                         sign = -1;
3139                 }
3140
3141                 uint32_t divisions = 0;
3142                 /* Convert the new x position to a sample within the source */
3143                 samplepos_t current_fr;
3144                 if (with_snap) {
3145                         current_fr = snap_pixel_to_sample (current_x, ensure_snap);
3146                         divisions = trackview.editor().get_grid_music_divisions (0);
3147                 } else {
3148                         current_fr = trackview.editor().pixel_to_sample (current_x);
3149                 }
3150
3151                 /* and then to beats */
3152                 const double e_qaf = tmap.exact_qn_at_sample (current_fr + midi_region()->position(), divisions);
3153                 const double quarter_note_start = _region->quarter_note() - midi_region()->start_beats();
3154                 const Temporal::Beats x_beats = Temporal::Beats (e_qaf - quarter_note_start);
3155
3156                 if (at_front && x_beats < canvas_note->note()->end_time()) {
3157                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
3158                         Temporal::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
3159                         len += canvas_note->note()->length();
3160
3161                         if (!!len) {
3162                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
3163                         }
3164                 }
3165
3166                 if (!at_front) {
3167                         Temporal::Beats len = std::max(Temporal::Beats(1 / 512.0),
3168                                                      x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
3169                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
3170                 }
3171
3172                 delete resize_rect;
3173                 delete (*i);
3174         }
3175
3176         _resize_data.clear();
3177         apply_diff(true);
3178 }
3179
3180 void
3181 MidiRegionView::abort_resizing ()
3182 {
3183         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
3184                 delete (*i)->resize_rect;
3185                 delete *i;
3186         }
3187
3188         _resize_data.clear ();
3189 }
3190
3191 void
3192 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
3193 {
3194         uint8_t new_velocity;
3195
3196         if (relative) {
3197                 new_velocity = event->note()->velocity() + velocity;
3198                 clamp_to_0_127(new_velocity);
3199         } else {
3200                 new_velocity = velocity;
3201         }
3202
3203         event->set_selected (event->selected()); // change color
3204
3205         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
3206 }
3207
3208 void
3209 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
3210 {
3211         uint8_t new_note;
3212
3213         if (relative) {
3214                 new_note = event->note()->note() + note;
3215         } else {
3216                 new_note = note;
3217         }
3218
3219         clamp_to_0_127 (new_note);
3220         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
3221 }
3222
3223 void
3224 MidiRegionView::trim_note (NoteBase* event, Temporal::Beats front_delta, Temporal::Beats end_delta)
3225 {
3226         bool change_start = false;
3227         bool change_length = false;
3228         Temporal::Beats new_start;
3229         Temporal::Beats new_length;
3230
3231         /* NOTE: the semantics of the two delta arguments are slightly subtle:
3232
3233            front_delta: if positive - move the start of the note later in time (shortening it)
3234            if negative - move the start of the note earlier in time (lengthening it)
3235
3236            end_delta:   if positive - move the end of the note later in time (lengthening it)
3237            if negative - move the end of the note earlier in time (shortening it)
3238         */
3239
3240         if (!!front_delta) {
3241                 if (front_delta < 0) {
3242
3243                         if (event->note()->time() < -front_delta) {
3244                                 new_start = Temporal::Beats();
3245                         } else {
3246                                 new_start = event->note()->time() + front_delta; // moves earlier
3247                         }
3248
3249                         /* start moved toward zero, so move the end point out to where it used to be.
3250                            Note that front_delta is negative, so this increases the length.
3251                         */
3252
3253                         new_length = event->note()->length() - front_delta;
3254                         change_start = true;
3255                         change_length = true;
3256
3257                 } else {
3258
3259                         Temporal::Beats new_pos = event->note()->time() + front_delta;
3260
3261                         if (new_pos < event->note()->end_time()) {
3262                                 new_start = event->note()->time() + front_delta;
3263                                 /* start moved toward the end, so move the end point back to where it used to be */
3264                                 new_length = event->note()->length() - front_delta;
3265                                 change_start = true;
3266                                 change_length = true;
3267                         }
3268                 }
3269
3270         }
3271
3272         if (!!end_delta) {
3273                 bool can_change = true;
3274                 if (end_delta < 0) {
3275                         if (event->note()->length() < -end_delta) {
3276                                 can_change = false;
3277                         }
3278                 }
3279
3280                 if (can_change) {
3281                         new_length = event->note()->length() + end_delta;
3282                         change_length = true;
3283                 }
3284         }
3285
3286         if (change_start) {
3287                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
3288         }
3289
3290         if (change_length) {
3291                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
3292         }
3293 }
3294
3295 void
3296 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
3297 {
3298         uint8_t new_channel;
3299
3300         if (relative) {
3301                 if (chn < 0.0) {
3302                         if (event->note()->channel() < -chn) {
3303                                 new_channel = 0;
3304                         } else {
3305                                 new_channel = event->note()->channel() + chn;
3306                         }
3307                 } else {
3308                         new_channel = event->note()->channel() + chn;
3309                 }
3310         } else {
3311                 new_channel = (uint8_t) chn;
3312         }
3313
3314         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
3315 }
3316
3317 void
3318 MidiRegionView::change_note_time (NoteBase* event, Temporal::Beats delta, bool relative)
3319 {
3320         Temporal::Beats new_time;
3321
3322         if (relative) {
3323                 if (delta < 0.0) {
3324                         if (event->note()->time() < -delta) {
3325                                 new_time = Temporal::Beats();
3326                         } else {
3327                                 new_time = event->note()->time() + delta;
3328                         }
3329                 } else {
3330                         new_time = event->note()->time() + delta;
3331                 }
3332         } else {
3333                 new_time = delta;
3334         }
3335
3336         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3337 }
3338
3339 void
3340 MidiRegionView::change_note_length (NoteBase* event, Temporal::Beats t)
3341 {
3342         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3343 }
3344
3345 void
3346 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3347 {
3348         int8_t delta;
3349         int8_t value = 0;
3350
3351         if (_selection.empty()) {
3352                 return;
3353         }
3354
3355         if (fine) {
3356                 delta = 1;
3357         } else {
3358                 delta = 10;
3359         }
3360
3361         if (!up) {
3362                 delta = -delta;
3363         }
3364
3365         if (!allow_smush) {
3366                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3367                         if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3368                                 goto cursor_label;
3369                         }
3370                 }
3371         }
3372
3373         start_note_diff_command (_("change velocities"));
3374
3375         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3376                 Selection::iterator next = i;
3377                 ++next;
3378
3379                 if (all_together) {
3380                         if (i == _selection.begin()) {
3381                                 change_note_velocity (*i, delta, true);
3382                                 value = (*i)->note()->velocity() + delta;
3383                         } else {
3384                                 change_note_velocity (*i, value, false);
3385                         }
3386
3387                 } else {
3388                         change_note_velocity (*i, delta, true);
3389                 }
3390
3391                 i = next;
3392         }
3393
3394         apply_diff();
3395
3396   cursor_label:
3397         if (!_selection.empty()) {
3398                 char buf[24];
3399                 snprintf (buf, sizeof (buf), "Vel %d",
3400                           (int) (*_selection.begin())->note()->velocity());
3401                 show_verbose_cursor (buf, 10, 10);
3402         }
3403 }
3404
3405
3406 void
3407 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3408 {
3409         if (_selection.empty()) {
3410                 return;
3411         }
3412
3413         int8_t delta;
3414
3415         if (fine) {
3416                 delta = 1;
3417         } else {
3418                 delta = 12;
3419         }
3420
3421         if (!up) {
3422                 delta = -delta;
3423         }
3424
3425         if (!allow_smush) {
3426                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3427                         if (!up) {
3428                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3429                                         return;
3430                                 }
3431                         } else {
3432                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
3433                                         return;
3434                                 }
3435                         }
3436                 }
3437         }
3438
3439         start_note_diff_command (_("transpose"));
3440
3441         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3442                 Selection::iterator next = i;
3443                 ++next;
3444                 change_note_note (*i, delta, true);
3445                 i = next;
3446         }
3447
3448         apply_diff ();
3449 }
3450
3451 void
3452 MidiRegionView::change_note_lengths (bool fine, bool shorter, Temporal::Beats delta, bool start, bool end)
3453 {
3454         if (!delta) {
3455                 if (fine) {
3456                         delta = Temporal::Beats(1.0/128.0);
3457                 } else {
3458                         /* grab the current grid distance */
3459                         delta = get_grid_beats(_region->position());
3460                 }
3461         }
3462
3463         if (shorter) {
3464                 delta = -delta;
3465         }
3466
3467         start_note_diff_command (_("change note lengths"));
3468
3469         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3470                 Selection::iterator next = i;
3471                 ++next;
3472
3473                 /* note the negation of the delta for start */
3474
3475                 trim_note (*i,
3476                            (start ? -delta : Temporal::Beats()),
3477                            (end   ? delta  : Temporal::Beats()));
3478                 i = next;
3479         }
3480
3481         apply_diff ();
3482
3483 }
3484
3485 void
3486 MidiRegionView::nudge_notes (bool forward, bool fine)
3487 {
3488         if (_selection.empty()) {
3489                 return;
3490         }
3491
3492         /* pick a note as the point along the timeline to get the nudge distance.
3493            its not necessarily the earliest note, so we may want to pull the notes out
3494            into a vector and sort before using the first one.
3495         */
3496
3497         const samplepos_t ref_point = source_beats_to_absolute_samples ((*(_selection.begin()))->note()->time());
3498         Temporal::Beats  delta;
3499
3500         if (!fine) {
3501
3502                 /* non-fine, move by 1 bar regardless of snap */
3503                 delta = Temporal::Beats(trackview.session()->tempo_map().meter_at_sample (ref_point).divisions_per_bar());
3504
3505         } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3506
3507                 /* grid is off - use nudge distance */
3508
3509                 samplepos_t       unused;
3510                 const samplecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3511                 delta = region_samples_to_region_beats (fabs ((double)distance));
3512
3513         } else {
3514
3515                 /* use grid */
3516
3517                 MusicSample next_pos (ref_point, 0);
3518                 if (forward) {
3519                         if (max_samplepos - 1 < next_pos.sample) {
3520                                 next_pos.sample += 1;
3521                         }
3522                 } else {
3523                         if (next_pos.sample == 0) {
3524                                 return;
3525                         }
3526                         next_pos.sample -= 1;
3527                 }
3528
3529                 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), SnapToGrid_Unscaled, false);
3530                 const samplecnt_t distance = ref_point - next_pos.sample;
3531                 delta = region_samples_to_region_beats (fabs ((double)distance));
3532         }
3533
3534         if (!delta) {
3535                 return;
3536         }
3537
3538         if (!forward) {
3539                 delta = -delta;
3540         }
3541
3542         start_note_diff_command (_("nudge"));
3543
3544         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3545                 Selection::iterator next = i;
3546                 ++next;
3547                 change_note_time (*i, delta, true);
3548                 i = next;
3549         }
3550
3551         apply_diff ();
3552 }
3553
3554 void
3555 MidiRegionView::change_channel(uint8_t channel)
3556 {
3557         start_note_diff_command(_("change channel"));
3558         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3559                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3560         }
3561
3562         apply_diff();
3563 }
3564
3565
3566 void
3567 MidiRegionView::note_entered(NoteBase* ev)
3568 {
3569         _entered_note = ev;
3570
3571         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3572
3573         if (_mouse_state == SelectTouchDragging) {
3574
3575                 note_selected (ev, true);
3576
3577         } else if (editor->current_mouse_mode() == MouseContent) {
3578
3579                 remove_ghost_note ();
3580                 show_verbose_cursor (ev->note ());
3581
3582         } else if (editor->current_mouse_mode() == MouseDraw) {
3583
3584                 remove_ghost_note ();
3585                 show_verbose_cursor (ev->note ());
3586         }
3587 }
3588
3589 void
3590 MidiRegionView::note_left (NoteBase*)
3591 {
3592         _entered_note = 0;
3593
3594         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3595                 (*i)->hide_velocity ();
3596         }
3597
3598         hide_verbose_cursor ();
3599 }
3600
3601 void
3602 MidiRegionView::patch_entered (PatchChange* p)
3603 {
3604         ostringstream s;
3605         s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3606           << instrument_info().get_patch_name_without (p->patch()->bank(), p->patch()->program(), p->patch()->channel()) << '\n'
3607           << _("Channel ") << ((int) p->patch()->channel() + 1);
3608         show_verbose_cursor (s.str(), 10, 20);
3609         p->item().grab_focus();
3610 }
3611
3612 void
3613 MidiRegionView::patch_left (PatchChange *)
3614 {
3615         hide_verbose_cursor ();
3616         /* focus will transfer back via the enter-notify event sent to this
3617          * midi region view.
3618          */
3619 }
3620
3621 void
3622 MidiRegionView::sysex_entered (SysEx* p)
3623 {
3624         // ostringstream s;
3625         // CAIROCANVAS
3626         // need a way to extract text from p->_flag->_text
3627         // s << p->text();
3628         // show_verbose_cursor (s.str(), 10, 20);
3629         p->item().grab_focus();
3630 }
3631
3632 void
3633 MidiRegionView::sysex_left (SysEx *)
3634 {
3635         hide_verbose_cursor ();
3636         /* focus will transfer back via the enter-notify event sent to this
3637          * midi region view.
3638          */
3639 }
3640
3641 void
3642 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3643 {
3644         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3645         Editing::MouseMode mm = editor->current_mouse_mode();
3646         bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3647
3648         Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3649         if (can_set_cursor && ctx) {
3650                 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3651                         ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3652                 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3653                         ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3654                 } else {
3655                         ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3656                 }
3657         }
3658 }
3659
3660 uint32_t
3661 MidiRegionView::get_fill_color() const
3662 {
3663         const std::string mod_name = (_dragging ? "dragging region" :
3664                                       trackview.editor().internal_editing() ? "editable region" :
3665                                       "midi frame base");
3666         if (_selected) {
3667                 return UIConfiguration::instance().color_mod ("selected region base", mod_name);
3668         } else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) &&
3669                    !UIConfiguration::instance().get_color_regions_using_track_color()) {
3670                 return UIConfiguration::instance().color_mod ("midi frame base", mod_name);
3671         }
3672         return UIConfiguration::instance().color_mod (fill_color, mod_name);
3673 }
3674
3675 void
3676 MidiRegionView::midi_channel_mode_changed ()
3677 {
3678         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3679         uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3680         ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3681
3682         if (mode == ForceChannel) {
3683                 mask = 0xFFFF; // Show all notes as active (below)
3684         }
3685
3686         // Update notes for selection
3687         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3688                 i->second->on_channel_selection_change (mask);
3689         }
3690
3691         _patch_changes.clear ();
3692         display_patch_changes ();
3693 }
3694
3695 void
3696 MidiRegionView::instrument_settings_changed ()
3697 {
3698         redisplay_model();
3699 }
3700
3701 void
3702 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3703 {
3704         if (_selection.empty()) {
3705                 return;
3706         }
3707
3708         PublicEditor& editor (trackview.editor());
3709
3710         switch (op) {
3711         case Delete:
3712                 /* XXX what to do ? */
3713                 break;
3714         case Cut:
3715         case Copy:
3716                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3717                 break;
3718         default:
3719                 break;
3720         }
3721
3722         if (op != Copy) {
3723
3724                 start_note_diff_command();
3725
3726                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3727                         switch (op) {
3728                         case Copy:
3729                                 break;
3730                         case Delete:
3731                         case Cut:
3732                         case Clear:
3733                                 note_diff_remove_note (*i);
3734                                 break;
3735                         }
3736                 }
3737
3738                 apply_diff();
3739         }
3740 }
3741
3742 MidiCutBuffer*
3743 MidiRegionView::selection_as_cut_buffer () const
3744 {
3745         Notes notes;
3746
3747         for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3748                 NoteType* n = (*i)->note().get();
3749                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3750         }
3751
3752         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3753         cb->set (notes);
3754
3755         return cb;
3756 }
3757
3758 /** This method handles undo */
3759 bool
3760 MidiRegionView::paste (samplepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
3761 {
3762         bool commit = false;
3763         // Paste notes, if available
3764         MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3765         if (m != selection.midi_notes.end()) {
3766                 ctx.counts.increase_n_notes();
3767                 if (!(*m)->empty()) {
3768                         commit = true;
3769                 }
3770                 paste_internal(pos, ctx.count, ctx.times, **m);
3771         }
3772
3773         // Paste control points to automation children, if available
3774         typedef RouteTimeAxisView::AutomationTracks ATracks;
3775         const ATracks& atracks = midi_view()->automation_tracks();
3776         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3777                 if (a->second->paste(pos, selection, ctx, sub_num)) {
3778                         if(!commit) {
3779                                 trackview.editor().begin_reversible_command (Operations::paste);
3780                         }
3781                         commit = true;
3782                 }
3783         }
3784
3785         if (commit) {
3786                 trackview.editor().commit_reversible_command ();
3787         }
3788         return true;
3789 }
3790
3791 /** This method handles undo */
3792 void
3793 MidiRegionView::paste_internal (samplepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3794 {
3795         if (mcb.empty()) {
3796                 return;
3797         }
3798
3799         start_note_diff_command (_("paste"));
3800
3801         const Temporal::Beats snap_beats    = get_grid_beats(pos);
3802         const Temporal::Beats first_time    = (*mcb.notes().begin())->time();
3803         const Temporal::Beats last_time     = (*mcb.notes().rbegin())->end_time();
3804         const Temporal::Beats duration      = last_time - first_time;
3805         const Temporal::Beats snap_duration = duration.snap_to(snap_beats);
3806         const Temporal::Beats paste_offset  = snap_duration * paste_count;
3807         const Temporal::Beats quarter_note  = absolute_samples_to_source_beats(pos) + paste_offset;
3808         Temporal::Beats       end_point     = Temporal::Beats();
3809
3810         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3811                                                        first_time,
3812                                                        last_time,
3813                                                        duration, pos, _region->position(),
3814                                                        quarter_note));
3815
3816         clear_editor_note_selection ();
3817
3818         for (int n = 0; n < (int) times; ++n) {
3819
3820                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3821
3822                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3823                         copied_note->set_time (quarter_note + copied_note->time() - first_time);
3824                         copied_note->set_id (Evoral::next_event_id());
3825
3826                         /* make all newly added notes selected */
3827
3828                         note_diff_add_note (copied_note, true);
3829                         end_point = copied_note->end_time();
3830                 }
3831         }
3832
3833         /* if we pasted past the current end of the region, extend the region */
3834
3835         samplepos_t end_sample = source_beats_to_absolute_samples (end_point);
3836         samplepos_t region_end = _region->position() + _region->length() - 1;
3837
3838         if (end_sample > region_end) {
3839
3840                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_sample));
3841
3842                 _region->clear_changes ();
3843                 /* we probably need to get the snap modifier somehow to make this correct for non-musical use */
3844                 _region->set_length (end_sample - _region->position(), trackview.editor().get_grid_music_divisions (0));
3845                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3846         }
3847
3848         apply_diff (true);
3849 }
3850
3851 struct EventNoteTimeEarlyFirstComparator {
3852         bool operator() (NoteBase* a, NoteBase* b) {
3853                 return a->note()->time() < b->note()->time();
3854         }
3855 };
3856
3857 void
3858 MidiRegionView::goto_next_note (bool add_to_selection)
3859 {
3860         bool use_next = false;
3861
3862         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3863         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3864         NoteBase* first_note = 0;
3865
3866         MidiModel::ReadLock lock(_model->read_lock());
3867         MidiModel::Notes& notes (_model->notes());
3868
3869         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
3870                 NoteBase* cne = 0;
3871                 if ((cne = find_canvas_note (*n))) {
3872
3873                         if (!first_note && (channel_mask & (1 << (*n)->channel()))) {
3874                                 first_note = cne;
3875                         }
3876
3877                         if (cne->selected()) {
3878                                 use_next = true;
3879                                 continue;
3880                         } else if (use_next) {
3881                                 if (channel_mask & (1 << (*n)->channel())) {
3882                                         if (!add_to_selection) {
3883                                                 unique_select (cne);
3884                                         } else {
3885                                                 note_selected (cne, true, false);
3886                                         }
3887
3888                                         return;
3889                                 }
3890                         }
3891                 }
3892         }
3893
3894         /* use the first one */
3895
3896         if (!_events.empty() && first_note) {
3897                 unique_select (first_note);
3898         }
3899 }
3900
3901 void
3902 MidiRegionView::goto_previous_note (bool add_to_selection)
3903 {
3904         bool use_next = false;
3905
3906         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3907         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3908         NoteBase* last_note = 0;
3909
3910         MidiModel::ReadLock lock(_model->read_lock());
3911         MidiModel::Notes& notes (_model->notes());
3912
3913         for (MidiModel::Notes::reverse_iterator n = notes.rbegin(); n != notes.rend(); ++n) {
3914                 NoteBase* cne = 0;
3915                 if ((cne = find_canvas_note (*n))) {
3916
3917                         if (!last_note && (channel_mask & (1 << (*n)->channel()))) {
3918                                 last_note = cne;
3919                         }
3920
3921                         if (cne->selected()) {
3922                                 use_next = true;
3923                                 continue;
3924
3925                         } else if (use_next) {
3926                                 if (channel_mask & (1 << (*n)->channel())) {
3927                                         if (!add_to_selection) {
3928                                                 unique_select (cne);
3929                                         } else {
3930                                                 note_selected (cne, true, false);
3931                                         }
3932
3933                                         return;
3934                                 }
3935                         }
3936                 }
3937         }
3938
3939         /* use the last one */
3940
3941         if (!_events.empty() && last_note) {
3942                 unique_select (last_note);
3943         }
3944 }
3945
3946 void
3947 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3948 {
3949         bool had_selected = false;
3950
3951         /* we previously time sorted events here, but Notes is a multiset sorted by time */
3952
3953         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3954                 if (i->second->selected()) {
3955                         selected.insert (i->first);
3956                         had_selected = true;
3957                 }
3958         }
3959
3960         if (allow_all_if_none_selected && !had_selected) {
3961                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3962                         selected.insert (i->first);
3963                 }
3964         }
3965 }
3966
3967 void
3968 MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
3969 {
3970         x = std::max(0.0, x);
3971
3972         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3973
3974         _last_ghost_x = x;
3975         _last_ghost_y = y;
3976
3977         _note_group->canvas_to_item (x, y);
3978
3979         PublicEditor& editor = trackview.editor ();
3980
3981         samplepos_t const unsnapped_sample = editor.pixel_to_sample (x);
3982
3983         const int32_t divisions = editor.get_grid_music_divisions (state);
3984         const bool shift_snap = midi_view()->note_mode() != Percussive;
3985         const Temporal::Beats snapped_beats = snap_sample_to_grid_underneath (unsnapped_sample, divisions, shift_snap);
3986
3987         /* prevent Percussive mode from displaying a ghost hit at region end */
3988         if (!shift_snap && snapped_beats >= midi_region()->start_beats() + midi_region()->length_beats()) {
3989                 _ghost_note->hide();
3990                 hide_verbose_cursor ();
3991                 return;
3992         }
3993
3994         /* ghost note may have been snapped before region */
3995         if (_ghost_note && snapped_beats.to_double() < 0.0) {
3996                 _ghost_note->hide();
3997                 return;
3998
3999         } else if (_ghost_note) {
4000                 _ghost_note->show();
4001         }
4002
4003         /* calculate time in beats relative to start of source */
4004         const Temporal::Beats length = get_grid_beats(unsnapped_sample + _region->position());
4005
4006         _ghost_note->note()->set_time (snapped_beats);
4007         _ghost_note->note()->set_length (length);
4008         _ghost_note->note()->set_note (y_to_note (y));
4009         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
4010         _ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats));
4011         /* the ghost note does not appear in ghost regions, so pass false in here */
4012         update_note (_ghost_note, false);
4013
4014         show_verbose_cursor (_ghost_note->note ());
4015 }
4016
4017 void
4018 MidiRegionView::create_ghost_note (double x, double y, uint32_t state)
4019 {
4020         remove_ghost_note ();
4021
4022         boost::shared_ptr<NoteType> g (new NoteType);
4023         if (midi_view()->note_mode() == Sustained) {
4024                 _ghost_note = new Note (*this, _note_group, g);
4025         } else {
4026                 _ghost_note = new Hit (*this, _note_group, 10, g);
4027         }
4028         _ghost_note->set_ignore_events (true);
4029         _ghost_note->set_outline_color (0x000000aa);
4030         update_ghost_note (x, y, state);
4031         _ghost_note->show ();
4032
4033         show_verbose_cursor (_ghost_note->note ());
4034 }
4035
4036 void
4037 MidiRegionView::remove_ghost_note ()
4038 {
4039         delete _ghost_note;
4040         _ghost_note = 0;
4041 }
4042
4043 void
4044 MidiRegionView::hide_verbose_cursor ()
4045 {
4046         trackview.editor().verbose_cursor()->hide ();
4047         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4048         if (mtv) {
4049                 mtv->set_note_highlight (NO_MIDI_NOTE);
4050         }
4051 }
4052
4053 void
4054 MidiRegionView::snap_changed ()
4055 {
4056         if (!_ghost_note) {
4057                 return;
4058         }
4059
4060         create_ghost_note (_last_ghost_x, _last_ghost_y, 0);
4061 }
4062
4063 void
4064 MidiRegionView::drop_down_keys ()
4065 {
4066         _mouse_state = None;
4067 }
4068
4069 void
4070 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
4071 {
4072         /* XXX: This is dead code.  What was it for? */
4073
4074         double note = y_to_note(y);
4075         Events e;
4076         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4077
4078         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
4079
4080         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
4081                 get_events (e, Evoral::Sequence<Temporal::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
4082         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
4083                 get_events (e, Evoral::Sequence<Temporal::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
4084         } else {
4085                 return;
4086         }
4087
4088         bool add_mrv_selection = false;
4089
4090         if (_selection.empty()) {
4091                 add_mrv_selection = true;
4092         }
4093
4094         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
4095                 if (_selection.insert (i->second).second) {
4096                         i->second->set_selected (true);
4097                 }
4098         }
4099
4100         if (add_mrv_selection) {
4101                 PublicEditor& editor (trackview.editor());
4102                 editor.get_selection().add (this);
4103         }
4104 }
4105
4106 void
4107 MidiRegionView::color_handler ()
4108 {
4109         RegionView::color_handler ();
4110
4111         _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
4112         _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
4113
4114         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
4115                 i->second->set_selected (i->second->selected()); // will change color
4116         }
4117
4118         /* XXX probably more to do here */
4119 }
4120
4121 void
4122 MidiRegionView::enable_display (bool yn)
4123 {
4124         RegionView::enable_display (yn);
4125 }
4126
4127 void
4128 MidiRegionView::show_step_edit_cursor (Temporal::Beats pos)
4129 {
4130         if (_step_edit_cursor == 0) {
4131                 ArdourCanvas::Item* const group = get_canvas_group();
4132
4133                 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
4134                 _step_edit_cursor->set_y0 (0);
4135                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
4136                 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
4137                 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
4138         }
4139
4140         move_step_edit_cursor (pos);
4141         _step_edit_cursor->show ();
4142 }
4143
4144 void
4145 MidiRegionView::move_step_edit_cursor (Temporal::Beats pos)
4146 {
4147         _step_edit_cursor_position = pos;
4148
4149         if (_step_edit_cursor) {
4150                 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_samples (pos));
4151                 _step_edit_cursor->set_x0 (pixel);
4152                 set_step_edit_cursor_width (_step_edit_cursor_width);
4153         }
4154 }
4155
4156 void
4157 MidiRegionView::hide_step_edit_cursor ()
4158 {
4159         if (_step_edit_cursor) {
4160                 _step_edit_cursor->hide ();
4161         }
4162 }
4163
4164 void
4165 MidiRegionView::set_step_edit_cursor_width (Temporal::Beats beats)
4166 {
4167         _step_edit_cursor_width = beats;
4168
4169         if (_step_edit_cursor) {
4170                 _step_edit_cursor->set_x1 (_step_edit_cursor->x0()
4171                                            + trackview.editor().sample_to_pixel (
4172                                              region_beats_to_region_samples (_step_edit_cursor_position + beats)
4173                                              - region_beats_to_region_samples (_step_edit_cursor_position)));
4174         }
4175 }
4176
4177 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
4178  *  @param w Source that the data will end up in.
4179  */
4180 void
4181 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
4182 {
4183         if (!_active_notes) {
4184                 /* we aren't actively being recorded to */
4185                 return;
4186         }
4187
4188         boost::shared_ptr<MidiSource> src = w.lock ();
4189         if (!src || src != midi_region()->midi_source()) {
4190                 /* recorded data was not destined for our source */
4191                 return;
4192         }
4193
4194         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
4195
4196         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
4197
4198         samplepos_t back = max_samplepos;
4199
4200         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
4201                 const Evoral::Event<MidiBuffer::TimeType>& ev = *i;
4202
4203                 if (ev.is_channel_event()) {
4204                         if (get_channel_mode() == FilterChannels) {
4205                                 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
4206                                         continue;
4207                                 }
4208                         }
4209                 }
4210
4211                 /* convert from session samples to source beats */
4212                 Temporal::Beats const time_beats = _source_relative_time_converter.from(
4213                         ev.time() - src->natural_position() + _region->start());
4214
4215                 if (ev.type() == MIDI_CMD_NOTE_ON) {
4216                         boost::shared_ptr<NoteType> note (
4217                                 new NoteType (ev.channel(), time_beats, Temporal::Beats(), ev.note(), ev.velocity()));
4218
4219                         add_note (note, true);
4220
4221                         /* fix up our note range */
4222                         if (ev.note() < _current_range_min) {
4223                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
4224                         } else if (ev.note() > _current_range_max) {
4225                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
4226                         }
4227
4228                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
4229                         resolve_note (ev.note (), time_beats);
4230                 }
4231
4232                 back = ev.time ();
4233         }
4234
4235         midi_stream_view()->check_record_layers (region(), back);
4236 }
4237
4238 void
4239 MidiRegionView::trim_front_starting ()
4240 {
4241         /* We used to eparent the note group to the region view's parent, so that it didn't change.
4242            now we update it.
4243         */
4244 }
4245
4246 void
4247 MidiRegionView::trim_front_ending ()
4248 {
4249         if (_region->start() < 0) {
4250                 /* Trim drag made start time -ve; fix this */
4251                 midi_region()->fix_negative_start ();
4252         }
4253 }
4254
4255 void
4256 MidiRegionView::edit_patch_change (PatchChange* pc)
4257 {
4258         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
4259
4260         int response = d.run();
4261
4262         switch (response) {
4263         case Gtk::RESPONSE_ACCEPT:
4264                 break;
4265         case Gtk::RESPONSE_REJECT:
4266                 delete_patch_change (pc);
4267                 return;
4268         default:
4269                 return;
4270         }
4271
4272         change_patch_change (pc->patch(), d.patch ());
4273 }
4274
4275 void
4276 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
4277 {
4278         // CAIROCANVAS
4279         // sysyex object doesn't have a pointer to a sysex event
4280         // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
4281         // c->remove (sysex->sysex());
4282         // _model->apply_command (*trackview.session(), c);
4283
4284         //_sys_exes.clear ();
4285         // display_sysexes();
4286 }
4287
4288 std::string
4289 MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
4290 {
4291         using namespace MIDI::Name;
4292         std::string name;
4293
4294         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4295         if (mtv) {
4296                 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
4297                 if (device_names) {
4298                         MIDI::Name::PatchPrimaryKey patch_key;
4299                         get_patch_key_at(n->time(), n->channel(), patch_key);
4300                         name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
4301                                                        n->channel(),
4302                                                        patch_key.bank(),
4303                                                        patch_key.program(),
4304                                                        note_value);
4305                 }
4306         }
4307
4308         char buf[128];
4309         snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
4310                   (int) note_value,
4311                   name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
4312                   (int) n->channel() + 1,
4313                   (int) n->velocity());
4314
4315         return buf;
4316 }
4317
4318 void
4319 MidiRegionView::show_verbose_cursor_for_new_note_value(boost::shared_ptr<NoteType> current_note,
4320                                                        uint8_t new_value) const
4321 {
4322         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4323         if (mtv) {
4324                 mtv->set_note_highlight (new_value);
4325         }
4326
4327         show_verbose_cursor(get_note_name(current_note, new_value), 10, 20);
4328 }
4329
4330 void
4331 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
4332 {
4333         show_verbose_cursor_for_new_note_value(n, n->note());
4334 }
4335
4336 void
4337 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
4338 {
4339         trackview.editor().verbose_cursor()->set (text);
4340         trackview.editor().verbose_cursor()->show ();
4341         trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
4342 }
4343
4344 uint8_t
4345 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
4346 {
4347         if (_model->notes().empty()) {
4348                 return 0x40;  // No notes, use default
4349         }
4350
4351         MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
4352         if (m == _model->notes().begin()) {
4353                 // Before the start, use the velocity of the first note
4354                 return (*m)->velocity();
4355         } else if (m == _model->notes().end()) {
4356                 // Past the end, use the velocity of the last note
4357                 --m;
4358                 return (*m)->velocity();
4359         }
4360
4361         // Interpolate velocity of surrounding notes
4362         MidiModel::Notes::const_iterator n = m;
4363         --n;
4364
4365         const double frac = ((time - (*n)->time()).to_double() /
4366                              ((*m)->time() - (*n)->time()).to_double());
4367
4368         return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4369 }
4370
4371 /** @param p A session samplepos.
4372  *  @param divisions beat division to snap given by Editor::get_grid_music_divisions() where
4373  *  bar is -1, 0 is audio samples and a positive integer is beat subdivisions.
4374  *  @return beat duration of p snapped to the grid subdivision underneath it.
4375  */
4376 Temporal::Beats
4377 MidiRegionView::snap_sample_to_grid_underneath (samplepos_t p, int32_t divisions, bool shift_snap) const
4378 {
4379         TempoMap& map (trackview.session()->tempo_map());
4380         double eqaf = map.exact_qn_at_sample (p + _region->position(), divisions);
4381
4382         if (divisions != 0 && shift_snap) {
4383                 const double qaf = map.quarter_note_at_sample (p + _region->position());
4384                 /* Hack so that we always snap to the note that we are over, instead of snapping
4385                    to the next one if we're more than halfway through the one we're over.
4386                 */
4387                 const Temporal::Beats grid_beats = get_grid_beats (p + _region->position());
4388                 const double rem = eqaf - qaf;
4389                 if (rem >= 0.0) {
4390                         eqaf -= grid_beats.to_double();
4391                 }
4392         }
4393         const double session_start_off = _region->quarter_note() - midi_region()->start_beats();
4394
4395         return Temporal::Beats (eqaf - session_start_off);
4396 }
4397
4398 ChannelMode
4399 MidiRegionView::get_channel_mode () const
4400 {
4401         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4402         return rtav->midi_track()->get_playback_channel_mode();
4403 }
4404
4405 uint16_t
4406 MidiRegionView::get_selected_channels () const
4407 {
4408         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4409         return rtav->midi_track()->get_playback_channel_mask();
4410 }
4411
4412
4413 Temporal::Beats
4414 MidiRegionView::get_grid_beats(samplepos_t pos) const
4415 {
4416         PublicEditor& editor  = trackview.editor();
4417         bool          success = false;
4418         Temporal::Beats beats   = editor.get_grid_type_as_beats (success, pos);
4419         if (!success) {
4420                 beats = Temporal::Beats(1);
4421         }
4422         return beats;
4423 }
4424 uint8_t
4425 MidiRegionView::y_to_note (double y) const
4426 {
4427         int const n = ((contents_height() - y) / contents_height() * (double)(_current_range_max - _current_range_min + 1))
4428                 + _current_range_min;
4429
4430         if (n < 0) {
4431                 return 0;
4432         } else if (n > 127) {
4433                 return 127;
4434         }
4435
4436         /* min due to rounding and/or off-by-one errors */
4437         return min ((uint8_t) n, _current_range_max);
4438 }
4439
4440 double
4441 MidiRegionView::note_to_y(uint8_t note) const
4442 {
4443         return contents_height() - (note + 1 - _current_range_min) * note_height() + 1;
4444 }
4445
4446 double
4447 MidiRegionView::session_relative_qn (double qn) const
4448 {
4449         return qn + (region()->quarter_note() - midi_region()->start_beats());
4450 }