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