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