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