fb598e5f286649f73454537cb011cd766fda691f
[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 <cassert>
22 #include <algorithm>
23 #include <ostream>
24
25 #include <gtkmm.h>
26
27 #include <gtkmm2ext/gtk_ui.h>
28
29 #include <sigc++/signal.h>
30
31 #include "pbd/memento_command.h"
32 #include "pbd/stateful_diff_command.h"
33
34 #include "ardour/playlist.h"
35 #include "ardour/tempo.h"
36 #include "ardour/midi_region.h"
37 #include "ardour/midi_source.h"
38 #include "ardour/midi_model.h"
39 #include "ardour/midi_patch_manager.h"
40 #include "ardour/session.h"
41
42 #include "evoral/Parameter.hpp"
43 #include "evoral/MIDIParameters.hpp"
44 #include "evoral/MIDIEvent.hpp"
45 #include "evoral/Control.hpp"
46 #include "evoral/midi_util.h"
47
48 #include "automation_region_view.h"
49 #include "automation_time_axis.h"
50 #include "canvas-hit.h"
51 #include "canvas-note.h"
52 #include "canvas_patch_change.h"
53 #include "debug.h"
54 #include "editor.h"
55 #include "editor_drag.h"
56 #include "ghostregion.h"
57 #include "gui_thread.h"
58 #include "keyboard.h"
59 #include "midi_channel_dialog.h"
60 #include "midi_cut_buffer.h"
61 #include "midi_list_editor.h"
62 #include "midi_region_view.h"
63 #include "midi_streamview.h"
64 #include "midi_time_axis.h"
65 #include "midi_util.h"
66 #include "note_player.h"
67 #include "public_editor.h"
68 #include "rgb_macros.h"
69 #include "selection.h"
70 #include "simpleline.h"
71 #include "streamview.h"
72 #include "utils.h"
73 #include "mouse_cursors.h"
74 #include "patch_change_dialog.h"
75 #include "verbose_cursor.h"
76
77 #include "i18n.h"
78
79 using namespace ARDOUR;
80 using namespace PBD;
81 using namespace Editing;
82 using namespace ArdourCanvas;
83 using Gtkmm2ext::Keyboard;
84
85 PBD::Signal1<void, MidiRegionView *> MidiRegionView::SelectionCleared;
86
87 #define MIDI_BP_ZERO ((Config->get_first_midi_bank_is_zero())?0:1)
88
89 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
90                                 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color const & basic_color)
91         : RegionView (parent, tv, r, spu, basic_color)
92         , _force_channel(-1)
93         , _last_channel_selection(0xFFFF)
94         , _current_range_min(0)
95         , _current_range_max(0)
96         , _active_notes(0)
97         , _note_group(new ArdourCanvas::Group(*group))
98         , _note_diff_command (0)
99         , _ghost_note(0)
100         , _step_edit_cursor (0)
101         , _step_edit_cursor_width (1.0)
102         , _step_edit_cursor_position (0.0)
103         , _channel_selection_scoped_note (0)
104         , _temporary_note_group (0)
105         , _mouse_state(None)
106         , _pressed_button(0)
107         , _sort_needed (true)
108         , _optimization_iterator (_events.end())
109         , _list_editor (0)
110         , _no_sound_notes (false)
111         , _last_event_x (0)
112         , _last_event_y (0)
113         , _pre_enter_cursor (0)
114 {
115         _note_group->raise_to_top();
116         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
117
118         Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
119         connect_to_diskstream ();
120
121         SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
122 }
123
124 MidiRegionView::MidiRegionView (ArdourCanvas::Group *parent, RouteTimeAxisView &tv,
125                                 boost::shared_ptr<MidiRegion> r, double spu, Gdk::Color& basic_color,
126                                 TimeAxisViewItem::Visibility visibility)
127         : RegionView (parent, tv, r, spu, basic_color, false, visibility)
128         , _force_channel(-1)
129         , _last_channel_selection(0xFFFF)
130         , _current_range_min(0)
131         , _current_range_max(0)
132         , _active_notes(0)
133         , _note_group(new ArdourCanvas::Group(*parent))
134         , _note_diff_command (0)
135         , _ghost_note(0)
136         , _step_edit_cursor (0)
137         , _step_edit_cursor_width (1.0)
138         , _step_edit_cursor_position (0.0)
139         , _channel_selection_scoped_note (0)
140         , _temporary_note_group (0)
141         , _mouse_state(None)
142         , _pressed_button(0)
143         , _sort_needed (true)
144         , _optimization_iterator (_events.end())
145         , _list_editor (0)
146         , _no_sound_notes (false)
147         , _last_event_x (0)
148         , _last_event_y (0)
149         , _pre_enter_cursor (0)
150 {
151         _note_group->raise_to_top();
152         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
153
154         connect_to_diskstream ();
155
156         SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
157 }
158
159 void
160 MidiRegionView::parameter_changed (std::string const & p)
161 {
162         if (p == "diplay-first-midi-bank-as-zero") {
163                 if (_enable_display) {
164                         redisplay_model();
165                 }
166         }
167 }
168
169 MidiRegionView::MidiRegionView (const MidiRegionView& other)
170         : sigc::trackable(other)
171         , RegionView (other)
172         , _force_channel(-1)
173         , _last_channel_selection(0xFFFF)
174         , _current_range_min(0)
175         , _current_range_max(0)
176         , _active_notes(0)
177         , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
178         , _note_diff_command (0)
179         , _ghost_note(0)
180         , _step_edit_cursor (0)
181         , _step_edit_cursor_width (1.0)
182         , _step_edit_cursor_position (0.0)
183         , _channel_selection_scoped_note (0)
184         , _temporary_note_group (0)
185         , _mouse_state(None)
186         , _pressed_button(0)
187         , _sort_needed (true)
188         , _optimization_iterator (_events.end())
189         , _list_editor (0)
190         , _no_sound_notes (false)
191         , _last_event_x (0)
192         , _last_event_y (0)
193         , _pre_enter_cursor (0)
194 {
195         Gdk::Color c;
196         int r,g,b,a;
197
198         UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
199         c.set_rgb_p (r/255.0, g/255.0, b/255.0);
200
201         init (c, false);
202 }
203
204 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
205         : RegionView (other, boost::shared_ptr<Region> (region))
206         , _force_channel(-1)
207         , _last_channel_selection(0xFFFF)
208         , _current_range_min(0)
209         , _current_range_max(0)
210         , _active_notes(0)
211         , _note_group(new ArdourCanvas::Group(*get_canvas_group()))
212         , _note_diff_command (0)
213         , _ghost_note(0)
214         , _step_edit_cursor (0)
215         , _step_edit_cursor_width (1.0)
216         , _step_edit_cursor_position (0.0)
217         , _channel_selection_scoped_note (0)
218         , _temporary_note_group (0)
219         , _mouse_state(None)
220         , _pressed_button(0)
221         , _sort_needed (true)
222         , _optimization_iterator (_events.end())
223         , _list_editor (0)
224         , _no_sound_notes (false)
225         , _last_event_x (0)
226         , _last_event_y (0)
227         , _pre_enter_cursor (0)
228 {
229         Gdk::Color c;
230         int r,g,b,a;
231
232         UINT_TO_RGBA (other.fill_color, &r, &g, &b, &a);
233         c.set_rgb_p (r/255.0, g/255.0, b/255.0);
234
235         init (c, true);
236 }
237
238 void
239 MidiRegionView::init (Gdk::Color const & basic_color, bool wfd)
240 {
241         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
242
243         CanvasNoteEvent::CanvasNoteEventDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
244                                                          ui_bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
245                                                          gui_context());
246
247         if (wfd) {
248                 midi_region()->midi_source(0)->load_model();
249         }
250
251         _model = midi_region()->midi_source(0)->model();
252         _enable_display = false;
253
254         RegionView::init (basic_color, false);
255
256         compute_colors (basic_color);
257
258         set_height (trackview.current_height());
259
260         region_muted ();
261         region_sync_changed ();
262         region_resized (ARDOUR::bounds_change);
263         region_locked ();
264
265         reset_width_dependent_items (_pixel_width);
266
267         set_colors ();
268
269         _enable_display = true;
270         if (_model) {
271                 if (wfd) {
272                         display_model (_model);
273                 }
274         }
275
276         group->raise_to_top();
277         group->signal_event().connect(
278                 sigc::mem_fun(this, &MidiRegionView::canvas_event), false);
279
280         midi_view()->signal_channel_mode_changed().connect(
281                 sigc::mem_fun(this, &MidiRegionView::midi_channel_mode_changed));
282
283         midi_view()->signal_midi_patch_settings_changed().connect(
284                 sigc::mem_fun(this, &MidiRegionView::midi_patch_settings_changed));
285
286         trackview.editor().SnapChanged.connect(snap_changed_connection, invalidator(*this),
287                                                ui_bind(&MidiRegionView::snap_changed, this),
288                                                gui_context());
289
290         Config->ParameterChanged.connect (*this, invalidator (*this), ui_bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
291         connect_to_diskstream ();
292
293         SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), ui_bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
294 }
295
296 const boost::shared_ptr<ARDOUR::MidiRegion>
297 MidiRegionView::midi_region() const
298 {
299         return boost::dynamic_pointer_cast<ARDOUR::MidiRegion>(_region);
300 }
301
302 void
303 MidiRegionView::connect_to_diskstream ()
304 {
305         midi_view()->midi_track()->DataRecorded.connect(
306                 *this, invalidator(*this),
307                 ui_bind(&MidiRegionView::data_recorded, this, _1),
308                 gui_context());
309 }
310
311 bool
312 MidiRegionView::canvas_event(GdkEvent* ev)
313 {
314         switch (ev->type) {
315         case GDK_ENTER_NOTIFY:
316         case GDK_LEAVE_NOTIFY:
317                 _last_event_x = ev->crossing.x;
318                 _last_event_y = ev->crossing.y;
319                 break;
320         case GDK_MOTION_NOTIFY:
321                 _last_event_x = ev->motion.x;
322                 _last_event_y = ev->motion.y;
323                 break;
324         default:
325                 break;
326         }
327
328         if (!trackview.editor().internal_editing()) {
329                 return false;
330         }
331
332         switch (ev->type) {
333         case GDK_SCROLL:
334                 return scroll (&ev->scroll);
335
336         case GDK_KEY_PRESS:
337                 return key_press (&ev->key);
338
339         case GDK_KEY_RELEASE:
340                 return key_release (&ev->key);
341
342         case GDK_BUTTON_PRESS:
343                 return button_press (&ev->button);
344
345         case GDK_2BUTTON_PRESS:
346                 return true;
347
348         case GDK_BUTTON_RELEASE:
349                 return button_release (&ev->button);
350
351         case GDK_ENTER_NOTIFY:
352                 return enter_notify (&ev->crossing);
353
354         case GDK_LEAVE_NOTIFY:
355                 return leave_notify (&ev->crossing);
356
357         case GDK_MOTION_NOTIFY:
358                 return motion (&ev->motion);
359
360         default:
361                 break;
362         }
363
364         return false;
365 }
366
367 void
368 MidiRegionView::remove_ghost_note ()
369 {
370         delete _ghost_note;
371         _ghost_note = 0;
372 }
373
374 bool
375 MidiRegionView::enter_notify (GdkEventCrossing* ev)
376 {
377         trackview.editor().MouseModeChanged.connect (
378                 _mouse_mode_connection, invalidator (*this), ui_bind (&MidiRegionView::mouse_mode_changed, this), gui_context ()
379                 );
380
381         if (trackview.editor().current_mouse_mode() == MouseRange && _mouse_state != AddDragging) {
382                 create_ghost_note (ev->x, ev->y);
383         }
384
385         if (!trackview.editor().internal_editing()) {
386                 Keyboard::magic_widget_drop_focus();
387         } else {
388                 Keyboard::magic_widget_grab_focus();
389                 group->grab_focus();
390         }
391
392         return false;
393 }
394
395 bool
396 MidiRegionView::leave_notify (GdkEventCrossing*)
397 {
398         _mouse_mode_connection.disconnect ();
399
400         trackview.editor().verbose_cursor()->hide ();
401         remove_ghost_note ();
402
403         return false;
404 }
405
406 void
407 MidiRegionView::mouse_mode_changed ()
408 {
409         if (trackview.editor().current_mouse_mode() == MouseRange && trackview.editor().internal_editing()) {
410                 create_ghost_note (_last_event_x, _last_event_y);
411         } else {
412                 remove_ghost_note ();
413                 trackview.editor().verbose_cursor()->hide ();
414         }
415
416         if (!trackview.editor().internal_editing()) {
417                 Keyboard::magic_widget_drop_focus();
418         } else {
419                 Keyboard::magic_widget_grab_focus();
420                 group->grab_focus();
421         }
422 }
423
424 bool
425 MidiRegionView::button_press (GdkEventButton* ev)
426 {
427         if (ev->button != 1) {
428                 return false;
429         }
430
431         if (_mouse_state != SelectTouchDragging) {
432
433                 _pressed_button = ev->button;
434                 _mouse_state = Pressed;
435
436                 return true;
437         }
438
439         _pressed_button = ev->button;
440
441         return true;
442 }
443
444 bool
445 MidiRegionView::button_release (GdkEventButton* ev)
446 {
447         double event_x, event_y;
448
449         if (ev->button != 1) {
450                 return false;
451         }
452
453         event_x = ev->x;
454         event_y = ev->y;
455
456         group->w2i(event_x, event_y);
457         group->ungrab(ev->time);
458
459         PublicEditor& editor = trackview.editor ();
460
461         switch (_mouse_state) {
462         case Pressed: // Clicked
463
464                 switch (editor.current_mouse_mode()) {
465                 case MouseObject:
466                 case MouseTimeFX:
467                         {
468                                 clear_selection();
469
470                                 if (Keyboard::is_insert_note_event(ev)) {
471
472                                         double event_x, event_y;
473
474                                         event_x = ev->x;
475                                         event_y = ev->y;
476                                         group->w2i(event_x, event_y);
477
478                                         bool success;
479                                         Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
480
481                                         if (!success) {
482                                                 beats = 1;
483                                         }
484
485                                         create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true, true);
486                                 }
487
488                                 break;
489                         }
490                 case MouseRange:
491                         {
492                                 bool success;
493                                 Evoral::MusicalTime beats = editor.get_grid_type_as_beats (success, editor.pixel_to_frame (event_x));
494
495                                 if (!success) {
496                                         beats = 1;
497                                 }
498
499                                 create_note_at (editor.pixel_to_frame (event_x), event_y, beats, true, true);
500
501                                 break;
502                         }
503                 default:
504                         break;
505                 }
506
507                 _mouse_state = None;
508                 break;
509
510         case SelectRectDragging:
511         case AddDragging:
512                 editor.drags()->end_grab ((GdkEvent *) ev);
513                 _mouse_state = None;
514                 create_ghost_note (ev->x, ev->y);
515                 break;
516
517
518         default:
519                 break;
520         }
521
522         return false;
523 }
524
525 bool
526 MidiRegionView::motion (GdkEventMotion* ev)
527 {
528         PublicEditor& editor = trackview.editor ();
529
530         if (!_ghost_note && editor.current_mouse_mode() != MouseRange
531             && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())
532             && _mouse_state != AddDragging) {
533
534                 create_ghost_note (ev->x, ev->y);
535         } else if (_ghost_note && editor.current_mouse_mode() != MouseRange
536                    && Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
537
538                 update_ghost_note (ev->x, ev->y);
539         } else if (_ghost_note && editor.current_mouse_mode() != MouseRange) {
540
541                 remove_ghost_note ();
542
543                 editor.verbose_cursor()->hide ();
544         } else if (_ghost_note && editor.current_mouse_mode() == MouseRange) {
545                 update_ghost_note (ev->x, ev->y);
546         }
547
548         /* any motion immediately hides velocity text that may have been visible */
549
550         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
551                 (*i)->hide_velocity ();
552         }
553
554         switch (_mouse_state) {
555         case Pressed:
556
557                 if (_pressed_button == 1 && editor.current_mouse_mode() == MouseObject
558                     && !Keyboard::modifier_state_contains (ev->state, Keyboard::insert_note_modifier())) {
559
560                         editor.drags()->set (new MidiRubberbandSelectDrag (dynamic_cast<Editor *> (&editor), this), (GdkEvent *) ev);
561                         _mouse_state = SelectRectDragging;
562                         return true;
563
564                 } else if (editor.internal_editing()) {
565
566                         editor.drags()->set (new NoteCreateDrag (dynamic_cast<Editor *> (&editor), group, this), (GdkEvent *) ev);
567                         _mouse_state = AddDragging;
568
569                         remove_ghost_note ();
570
571                         editor.verbose_cursor()->hide ();
572
573                         return true;
574                 }
575
576                 return false;
577
578         case SelectRectDragging:
579         case AddDragging:
580                 editor.drags()->motion_handler ((GdkEvent *) ev, false);
581                 break;
582                 
583         case SelectTouchDragging:
584                 return false;
585
586         default:
587                 break;
588         }
589
590         return false;
591 }
592
593
594 bool
595 MidiRegionView::scroll (GdkEventScroll* ev)
596 {
597         if (_selection.empty()) {
598                 return false;
599         }
600
601         if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
602                 /* XXX: bit of a hack; allow PrimaryModifier scroll through so that
603                    it still works for zoom.
604                 */
605                 return false;
606         }
607
608         trackview.editor().verbose_cursor()->hide ();
609
610         bool fine = !Keyboard::modifier_state_equals (ev->state, Keyboard::SecondaryModifier);
611
612         if (ev->direction == GDK_SCROLL_UP) {
613                 change_velocities (true, fine, false);
614         } else if (ev->direction == GDK_SCROLL_DOWN) {
615                 change_velocities (false, fine, false);
616         }
617         return true;
618 }
619
620 bool
621 MidiRegionView::key_press (GdkEventKey* ev)
622 {
623         /* since GTK bindings are generally activated on press, and since
624            detectable auto-repeat is the name of the game and only sends
625            repeated presses, carry out key actions at key press, not release.
626         */
627
628         bool unmodified = Keyboard::no_modifier_keys_pressed (ev);
629         
630         if (unmodified && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
631                 _mouse_state = SelectTouchDragging;
632                 return true;
633
634         } else if (ev->keyval == GDK_Escape && unmodified) {
635                 clear_selection();
636                 _mouse_state = None;
637
638         } else if (unmodified && (ev->keyval == GDK_comma || ev->keyval == GDK_period)) {
639
640                 bool start = (ev->keyval == GDK_comma);
641                 bool end = (ev->keyval == GDK_period);
642                 bool shorter = Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier);
643                 bool fine = Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
644
645                 change_note_lengths (fine, shorter, 0.0, start, end);
646
647                 return true;
648
649         } else if (ev->keyval == GDK_Delete && unmodified) {
650
651                 delete_selection();
652                 return true;
653
654         } else if (ev->keyval == GDK_Tab) {
655
656                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
657                         goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
658                 } else {
659                         goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
660                 }
661                 return true;
662
663         } else if (ev->keyval == GDK_ISO_Left_Tab) {
664
665                 /* Shift-TAB generates ISO Left Tab, for some reason */
666
667                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
668                         goto_previous_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
669                 } else {
670                         goto_next_note (Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier));
671                 }
672                 return true;
673
674
675
676         } else if (ev->keyval == GDK_Up) {
677
678                 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
679                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
680
681                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
682                         change_velocities (true, fine, allow_smush);
683                 } else {
684                         transpose (true, fine, allow_smush);
685                 }
686                 return true;
687
688         } else if (ev->keyval == GDK_Down) {
689
690                 bool allow_smush = Keyboard::modifier_state_contains (ev->state, Keyboard::TertiaryModifier);
691                 bool fine = !Keyboard::modifier_state_contains (ev->state, Keyboard::SecondaryModifier);
692
693                 if (Keyboard::modifier_state_contains (ev->state, Keyboard::PrimaryModifier)) {
694                         change_velocities (false, fine, allow_smush);
695                 } else {
696                         transpose (false, fine, allow_smush);
697                 }
698                 return true;
699
700         } else if (ev->keyval == GDK_Left && unmodified) {
701
702                 nudge_notes (false);
703                 return true;
704
705         } else if (ev->keyval == GDK_Right && unmodified) {
706
707                 nudge_notes (true);
708                 return true;
709
710         } else if (ev->keyval == GDK_c && unmodified) {
711                 channel_edit ();
712                 return true;
713         }
714
715         return false;
716 }
717
718 bool
719 MidiRegionView::key_release (GdkEventKey* ev)
720 {
721         if ((_mouse_state == SelectTouchDragging) && (ev->keyval == GDK_Alt_L || ev->keyval == GDK_Alt_R)) {
722                 _mouse_state = None;
723                 return true;
724         }
725         return false;
726 }
727
728 void
729 MidiRegionView::channel_edit ()
730 {
731         bool first = true;
732         uint8_t current_channel = 0;
733
734         if (_selection.empty()) {
735                 return;
736         }
737         
738         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
739                 if (first) {
740                         current_channel = (*i)->note()->channel ();
741                         first = false;
742                 }
743         }
744
745         MidiChannelDialog channel_dialog (current_channel);
746         int ret = channel_dialog.run ();
747
748         switch (ret) {
749         case Gtk::RESPONSE_OK:
750                 break;
751         default:
752                 return;
753         }
754
755         uint8_t new_channel = channel_dialog.active_channel ();
756
757         start_note_diff_command (_("channel edit"));
758
759         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
760                 Selection::iterator next = i;
761                 ++next;
762                 change_note_channel (*i, new_channel);
763                 i = next;
764         }
765
766         apply_diff ();
767 }
768
769 void
770 MidiRegionView::show_list_editor ()
771 {
772         if (!_list_editor) {
773                 _list_editor = new MidiListEditor (trackview.session(), midi_region());
774         }
775         _list_editor->present ();
776 }
777
778 /** Add a note to the model, and the view, at a canvas (click) coordinate.
779  * \param t time in frames relative to the position of the region
780  * \param y vertical position in pixels
781  * \param length duration of the note in beats, which will be snapped to the grid
782  * \param sh true to make the note 1 frame shorter than the snapped version of \a length.
783  * \param snap_x true to snap x to the grid, otherwise false.
784  */
785 void
786 MidiRegionView::create_note_at (framepos_t t, double y, double length, bool sh, bool snap_x)
787 {
788         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
789         MidiStreamView* const view = mtv->midi_view();
790
791         double note = view->y_to_note(y);
792
793         assert(note >= 0.0);
794         assert(note <= 127.0);
795
796         // Start of note in frames relative to region start
797         if (snap_x) {
798                 framecnt_t grid_frames;
799                 t = snap_frame_to_grid_underneath (t, grid_frames);
800         }
801         assert (t >= 0);
802
803         // Snap length
804         length = region_frames_to_region_beats(
805                 snap_frame_to_frame (t + region_beats_to_region_frames(length)) - t
806                 );
807
808         assert (length != 0);
809
810         if (sh) {
811                 length = region_frames_to_region_beats (region_beats_to_region_frames (length) - 1);
812         }
813
814         const boost::shared_ptr<NoteType> new_note (new NoteType (mtv->get_channel_for_add (),
815                                                                   region_frames_to_region_beats(t + _region->start()), length,
816                                                                   (uint8_t)note, 0x40));
817
818         if (_model->contains (new_note)) {
819                 return;
820         }
821
822         view->update_note_range(new_note->note());
823
824         MidiModel::NoteDiffCommand* cmd = _model->new_note_diff_command("add note");
825         cmd->add (new_note);
826         _model->apply_command(*trackview.session(), cmd);
827
828         play_midi_note (new_note);
829 }
830
831 void
832 MidiRegionView::clear_events()
833 {
834         clear_selection();
835
836         MidiGhostRegion* gr;
837         for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
838                 if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
839                         gr->clear_events();
840                 }
841         }
842
843         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
844                 delete *i;
845         }
846
847         _events.clear();
848         _patch_changes.clear();
849         _sys_exes.clear();
850         _optimization_iterator = _events.end();
851 }
852
853 void
854 MidiRegionView::display_model(boost::shared_ptr<MidiModel> model)
855 {
856         _model = model;
857
858         content_connection.disconnect ();
859         _model->ContentsChanged.connect (content_connection, invalidator (*this), boost::bind (&MidiRegionView::redisplay_model, this), gui_context());
860
861         clear_events ();
862
863         if (_enable_display) {
864                 redisplay_model();
865         }
866 }
867
868 void
869 MidiRegionView::start_note_diff_command (string name)
870 {
871         if (!_note_diff_command) {
872                 _note_diff_command = _model->new_note_diff_command (name);
873         }
874 }
875
876 void
877 MidiRegionView::note_diff_add_note (const boost::shared_ptr<NoteType> note, bool selected, bool show_velocity)
878 {
879         if (_note_diff_command) {
880                 _note_diff_command->add (note);
881         }
882         if (selected) {
883                 _marked_for_selection.insert(note);
884         }
885         if (show_velocity) {
886                 _marked_for_velocity.insert(note);
887         }
888 }
889
890 void
891 MidiRegionView::note_diff_remove_note (ArdourCanvas::CanvasNoteEvent* ev)
892 {
893         if (_note_diff_command && ev->note()) {
894                 _note_diff_command->remove(ev->note());
895         }
896 }
897
898 void
899 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
900                                       MidiModel::NoteDiffCommand::Property property,
901                                       uint8_t val)
902 {
903         if (_note_diff_command) {
904                 _note_diff_command->change (ev->note(), property, val);
905         }
906 }
907
908 void
909 MidiRegionView::note_diff_add_change (ArdourCanvas::CanvasNoteEvent* ev,
910                                       MidiModel::NoteDiffCommand::Property property,
911                                       Evoral::MusicalTime val)
912 {
913         if (_note_diff_command) {
914                 _note_diff_command->change (ev->note(), property, val);
915         }
916 }
917
918 void
919 MidiRegionView::apply_diff (bool as_subcommand)
920 {
921         bool add_or_remove;
922
923         if (!_note_diff_command) {
924                 return;
925         }
926
927         if ((add_or_remove = _note_diff_command->adds_or_removes())) {
928                 // Mark all selected notes for selection when model reloads
929                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
930                         _marked_for_selection.insert((*i)->note());
931                 }
932         }
933
934         if (as_subcommand) {
935                 _model->apply_command_as_subcommand (*trackview.session(), _note_diff_command);
936         } else {
937                 _model->apply_command (*trackview.session(), _note_diff_command);
938         }
939
940         _note_diff_command = 0;
941         midi_view()->midi_track()->playlist_modified();
942
943         if (add_or_remove) {
944                 _marked_for_selection.clear();
945         }
946
947         _marked_for_velocity.clear();
948 }
949
950 void
951 MidiRegionView::abort_command()
952 {
953         delete _note_diff_command;
954         _note_diff_command = 0;
955         clear_selection();
956 }
957
958 CanvasNoteEvent*
959 MidiRegionView::find_canvas_note (boost::shared_ptr<NoteType> note)
960 {
961         if (_optimization_iterator != _events.end()) {
962                 ++_optimization_iterator;
963         }
964
965         if (_optimization_iterator != _events.end() && (*_optimization_iterator)->note() == note) {
966                 return *_optimization_iterator;
967         }
968
969         for (_optimization_iterator = _events.begin(); _optimization_iterator != _events.end(); ++_optimization_iterator) {
970                 if ((*_optimization_iterator)->note() == note) {
971                         return *_optimization_iterator;
972                 }
973         }
974
975         return 0;
976 }
977
978 void
979 MidiRegionView::get_events (Events& e, Evoral::Sequence<Evoral::MusicalTime>::NoteOperator op, uint8_t val, int chan_mask)
980 {
981         MidiModel::Notes notes;
982         _model->get_notes (notes, op, val, chan_mask);
983
984         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
985                 CanvasNoteEvent* cne = find_canvas_note (*n);
986                 if (cne) {
987                         e.push_back (cne);
988                 }
989         }
990 }
991
992 void
993 MidiRegionView::redisplay_model()
994 {
995         // Don't redisplay the model if we're currently recording and displaying that
996         if (_active_notes) {
997                 return;
998         }
999
1000         if (!_model) {
1001                 return;
1002         }
1003
1004         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1005                 (*i)->invalidate ();
1006         }
1007
1008         MidiModel::ReadLock lock(_model->read_lock());
1009
1010         MidiModel::Notes& notes (_model->notes());
1011         _optimization_iterator = _events.begin();
1012
1013         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
1014
1015                 boost::shared_ptr<NoteType> note (*n);
1016                 CanvasNoteEvent* cne;
1017                 bool visible;
1018
1019                 if (note_in_region_range (note, visible)) {
1020
1021                         if ((cne = find_canvas_note (note)) != 0) {
1022
1023                                 cne->validate ();
1024
1025                                 CanvasNote* cn;
1026                                 CanvasHit* ch;
1027
1028                                 if ((cn = dynamic_cast<CanvasNote*>(cne)) != 0) {
1029                                         update_note (cn);
1030                                 } else if ((ch = dynamic_cast<CanvasHit*>(cne)) != 0) {
1031                                         update_hit (ch);
1032                                 }
1033
1034                                 if (visible) {
1035                                         cne->show ();
1036                                 } else {
1037                                         cne->hide ();
1038                                 }
1039
1040                         } else {
1041
1042                                 add_note (note, visible);
1043                         }
1044
1045                 } else {
1046
1047                         if ((cne = find_canvas_note (note)) != 0) {
1048                                 cne->validate ();
1049                                 cne->hide ();
1050                         }
1051                 }
1052         }
1053
1054
1055         /* remove note items that are no longer valid */
1056
1057         for (Events::iterator i = _events.begin(); i != _events.end(); ) {
1058                 if (!(*i)->valid ()) {
1059
1060                         for (vector<GhostRegion*>::iterator j = ghosts.begin(); j != ghosts.end(); ++j) {
1061                                 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*j);
1062                                 if (gr) {
1063                                         gr->remove_note (*i);
1064                                 }
1065                         }
1066                         
1067                         delete *i;
1068                         i = _events.erase (i);
1069                         
1070                 } else {
1071                         ++i;
1072                 }
1073         }
1074
1075         _patch_changes.clear();
1076         _sys_exes.clear();
1077
1078         display_sysexes();
1079         display_patch_changes ();
1080
1081         _marked_for_selection.clear ();
1082         _marked_for_velocity.clear ();
1083
1084         /* we may have caused _events to contain things out of order (e.g. if a note
1085            moved earlier or later). we don't generally need them in time order, but
1086            make a note that a sort is required for those cases that require it.
1087         */
1088
1089         _sort_needed = true;
1090 }
1091
1092 void
1093 MidiRegionView::display_patch_changes ()
1094 {
1095         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1096         uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
1097
1098         for (uint8_t i = 0; i < 16; ++i) {
1099                 if (chn_mask & (1<<i)) {
1100                         display_patch_changes_on_channel (i);
1101                 }
1102                 /* TODO gray-out patch instad of not displaying it */
1103         }
1104 }
1105
1106 void
1107 MidiRegionView::display_patch_changes_on_channel (uint8_t channel)
1108 {
1109         for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1110
1111                 if ((*i)->channel() != channel) {
1112                         continue;
1113                 }
1114
1115                 MIDI::Name::PatchPrimaryKey patch_key ((*i)->bank_msb (), (*i)->bank_lsb (), (*i)->program ());
1116
1117                 boost::shared_ptr<MIDI::Name::Patch> patch =
1118                         MIDI::Name::MidiPatchManager::instance().find_patch(
1119                                 _model_name, _custom_device_mode, channel, patch_key);
1120
1121                 if (patch != 0) {
1122                         add_canvas_patch_change (*i, patch->name());
1123                 } else {
1124                         char buf[16];
1125                         /* program and bank numbers are zero-based: convert to one-based: MIDI_BP_ZERO */
1126                         snprintf (buf, 16, "%d %d", (*i)->program() + MIDI_BP_ZERO , (*i)->bank() + MIDI_BP_ZERO);
1127                         add_canvas_patch_change (*i, buf);
1128                 }
1129         }
1130 }
1131
1132 void
1133 MidiRegionView::display_sysexes()
1134 {
1135         bool have_periodic_system_messages = false;
1136         bool display_periodic_messages = true;
1137
1138         if (!Config->get_never_display_periodic_midi()) {
1139
1140                 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1141                         const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev = 
1142                                 boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1143                         
1144                         if (mev) {
1145                                 if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1146                                         have_periodic_system_messages = true;
1147                                         break;
1148                                 }
1149                         }
1150                 }
1151                 
1152                 if (have_periodic_system_messages) {
1153                         double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1154                         
1155                         /* get an approximate value for the number of samples per video frame */
1156                         
1157                         double video_frame = trackview.session()->frame_rate() * (1.0/30);
1158                         
1159                         /* if we are zoomed out beyond than the cutoff (i.e. more
1160                          * frames per pixel than frames per 4 video frames), don't
1161                          * show periodic sysex messages.
1162                          */
1163                         
1164                         if (zoom > (video_frame*4)) {
1165                                 display_periodic_messages = false;
1166                         } 
1167                 }
1168         } else {
1169                 display_periodic_messages = false;
1170         }
1171
1172         for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1173
1174                 const boost::shared_ptr<const Evoral::MIDIEvent<Evoral::MusicalTime> > mev = 
1175                         boost::static_pointer_cast<const Evoral::MIDIEvent<Evoral::MusicalTime> > (*i);
1176
1177                 Evoral::MusicalTime time = (*i)->time();
1178                 assert (time >= 0);
1179
1180                 if (mev) {
1181                         if (mev->is_spp() || mev->is_mtc_quarter() || mev->is_mtc_full()) {
1182                                 if (!display_periodic_messages) {
1183                                         continue;
1184                                 }
1185                         }
1186                 }
1187
1188                 ostringstream str;
1189                 str << hex;
1190                 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1191                         str << int((*i)->buffer()[b]);
1192                         if (b != (*i)->size() -1) {
1193                                 str << " ";
1194                         }
1195                 }
1196                 string text = str.str();
1197
1198                 const double x = trackview.editor().frame_to_pixel(source_beats_to_absolute_frames(time));
1199
1200                 double height = midi_stream_view()->contents_height();
1201
1202                 boost::shared_ptr<CanvasSysEx> sysex = boost::shared_ptr<CanvasSysEx>(
1203                         new CanvasSysEx(*this, *_note_group, text, height, x, 1.0));
1204
1205                 // Show unless message is beyond the region bounds
1206                 if (time - _region->start() >= _region->length() || time < _region->start()) {
1207                         sysex->hide();
1208                 } else {
1209                         sysex->show();
1210                 }
1211
1212                 _sys_exes.push_back(sysex);
1213         }
1214 }
1215
1216 MidiRegionView::~MidiRegionView ()
1217 {
1218         in_destructor = true;
1219
1220         trackview.editor().verbose_cursor()->hide ();
1221
1222         note_delete_connection.disconnect ();
1223
1224         delete _list_editor;
1225
1226         RegionViewGoingAway (this); /* EMIT_SIGNAL */
1227
1228         if (_active_notes) {
1229                 end_write();
1230         }
1231
1232         _selection.clear();
1233         clear_events();
1234
1235         delete _note_group;
1236         delete _note_diff_command;
1237         delete _step_edit_cursor;
1238         delete _temporary_note_group;
1239 }
1240
1241 void
1242 MidiRegionView::region_resized (const PropertyChange& what_changed)
1243 {
1244         RegionView::region_resized(what_changed);
1245
1246         if (what_changed.contains (ARDOUR::Properties::position)) {
1247                 set_duration(_region->length(), 0);
1248                 if (_enable_display) {
1249                         redisplay_model();
1250                 }
1251         }
1252 }
1253
1254 void
1255 MidiRegionView::reset_width_dependent_items (double pixel_width)
1256 {
1257         RegionView::reset_width_dependent_items(pixel_width);
1258         assert(_pixel_width == pixel_width);
1259
1260         if (_enable_display) {
1261                 redisplay_model();
1262         }
1263
1264         move_step_edit_cursor (_step_edit_cursor_position);
1265         set_step_edit_cursor_width (_step_edit_cursor_width);
1266 }
1267
1268 void
1269 MidiRegionView::set_height (double height)
1270 {
1271         static const double FUDGE = 2.0;
1272         const double old_height = _height;
1273         RegionView::set_height(height);
1274         _height = height - FUDGE;
1275
1276         apply_note_range(midi_stream_view()->lowest_note(),
1277                          midi_stream_view()->highest_note(),
1278                          height != old_height + FUDGE);
1279
1280         if (name_pixbuf) {
1281                 name_pixbuf->raise_to_top();
1282         }
1283
1284         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1285                 (*x)->set_height (midi_stream_view()->contents_height());
1286         }
1287
1288         if (_step_edit_cursor) {
1289                 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
1290         }
1291 }
1292
1293
1294 /** Apply the current note range from the stream view
1295  * by repositioning/hiding notes as necessary
1296  */
1297 void
1298 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1299 {
1300         if (!_enable_display) {
1301                 return;
1302         }
1303
1304         if (!force && _current_range_min == min && _current_range_max == max) {
1305                 return;
1306         }
1307
1308         _current_range_min = min;
1309         _current_range_max = max;
1310
1311         for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1312                 CanvasNoteEvent* event = *i;
1313                 boost::shared_ptr<NoteType> note (event->note());
1314
1315                 if (note->note() < _current_range_min ||
1316                     note->note() > _current_range_max) {
1317                         event->hide();
1318                 } else {
1319                         event->show();
1320                 }
1321
1322                 if (CanvasNote* cnote = dynamic_cast<CanvasNote*>(event)) {
1323
1324                         const double y1 = midi_stream_view()->note_to_y(note->note());
1325                         const double y2 = y1 + floor(midi_stream_view()->note_height());
1326
1327                         cnote->property_y1() = y1;
1328                         cnote->property_y2() = y2;
1329
1330                 } else if (CanvasHit* chit = dynamic_cast<CanvasHit*>(event)) {
1331
1332                         const double diamond_size = update_hit (chit);
1333
1334                         chit->set_height (diamond_size);
1335                 }
1336         }
1337 }
1338
1339 GhostRegion*
1340 MidiRegionView::add_ghost (TimeAxisView& tv)
1341 {
1342         CanvasNote* note;
1343
1344         double unit_position = _region->position () / samples_per_unit;
1345         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1346         MidiGhostRegion* ghost;
1347
1348         if (mtv && mtv->midi_view()) {
1349                 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1350                    to allow having midi notes on top of note lines and waveforms.
1351                 */
1352                 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1353         } else {
1354                 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1355         }
1356
1357         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1358                 if ((note = dynamic_cast<CanvasNote*>(*i)) != 0) {
1359                         ghost->add_note(note);
1360                 }
1361         }
1362
1363         ghost->set_height ();
1364         ghost->set_duration (_region->length() / samples_per_unit);
1365         ghosts.push_back (ghost);
1366
1367         GhostRegion::CatchDeletion.connect (*this, invalidator (*this), ui_bind (&RegionView::remove_ghost, this, _1), gui_context());
1368
1369         return ghost;
1370 }
1371
1372
1373 /** Begin tracking note state for successive calls to add_event
1374  */
1375 void
1376 MidiRegionView::begin_write()
1377 {
1378         assert(!_active_notes);
1379         _active_notes = new CanvasNote*[128];
1380         for (unsigned i=0; i < 128; ++i) {
1381                 _active_notes[i] = 0;
1382         }
1383 }
1384
1385
1386 /** Destroy note state for add_event
1387  */
1388 void
1389 MidiRegionView::end_write()
1390 {
1391         delete[] _active_notes;
1392         _active_notes = 0;
1393         _marked_for_selection.clear();
1394         _marked_for_velocity.clear();
1395 }
1396
1397
1398 /** Resolve an active MIDI note (while recording).
1399  */
1400 void
1401 MidiRegionView::resolve_note(uint8_t note, double end_time)
1402 {
1403         if (midi_view()->note_mode() != Sustained) {
1404                 return;
1405         }
1406
1407         if (_active_notes && _active_notes[note]) {
1408
1409                 /* XXX is end_time really region-centric? I think so, because
1410                    this is a new region that we're recording, so source zero is
1411                    the same as region zero
1412                 */
1413                 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1414
1415                 _active_notes[note]->property_x2() = trackview.editor().frame_to_pixel(end_time_frames);
1416                 _active_notes[note]->property_outline_what() = (guint32) 0xF; // all edges
1417                 _active_notes[note] = 0;
1418         }
1419 }
1420
1421
1422 /** Extend active notes to rightmost edge of region (if length is changed)
1423  */
1424 void
1425 MidiRegionView::extend_active_notes()
1426 {
1427         if (!_active_notes) {
1428                 return;
1429         }
1430
1431         for (unsigned i=0; i < 128; ++i) {
1432                 if (_active_notes[i]) {
1433                         _active_notes[i]->property_x2() = trackview.editor().frame_to_pixel(_region->length());
1434                 }
1435         }
1436 }
1437
1438
1439 void
1440 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1441 {
1442         if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1443                 return;
1444         }
1445
1446         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1447
1448         if (!route_ui || !route_ui->midi_track()) {
1449                 return;
1450         }
1451
1452         NotePlayer* np = new NotePlayer (route_ui->midi_track());
1453         np->add (note);
1454         np->play ();
1455 }
1456
1457 void
1458 MidiRegionView::play_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1459 {
1460         if (_no_sound_notes || !Config->get_sound_midi_notes()) {
1461                 return;
1462         }
1463
1464         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1465
1466         if (!route_ui || !route_ui->midi_track()) {
1467                 return;
1468         }
1469
1470         NotePlayer* np = new NotePlayer (route_ui->midi_track());
1471
1472         for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1473                 np->add (*n);
1474         }
1475
1476         np->play ();
1477 }
1478
1479
1480 bool
1481 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1482 {
1483         const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1484         bool outside = (note_start_frames  < 0) || (note_start_frames > _region->last_frame());
1485
1486         visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1487                 (note->note() <= midi_stream_view()->highest_note());
1488
1489         return !outside;
1490 }
1491
1492 /** Update a canvas note's size from its model note.
1493  *  @param ev Canvas note to update.
1494  *  @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1495  */
1496 void
1497 MidiRegionView::update_note (CanvasNote* ev, bool update_ghost_regions)
1498 {
1499         boost::shared_ptr<NoteType> note = ev->note();
1500
1501         const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (note->time()));
1502         const double y1 = midi_stream_view()->note_to_y(note->note());
1503
1504         ev->property_x1() = x;
1505         ev->property_y1() = y1;
1506
1507         /* trim note display to not overlap the end of its region */
1508
1509         if (note->length() > 0) {
1510                 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1511                 ev->property_x2() = trackview.editor().frame_to_pixel (note_end_frames);
1512         } else {
1513                 ev->property_x2() = trackview.editor().frame_to_pixel (_region->length());
1514         }
1515
1516         ev->property_y2() = y1 + floor(midi_stream_view()->note_height());
1517
1518         if (note->length() == 0) {
1519                 if (_active_notes) {
1520                         assert(note->note() < 128);
1521                         // If this note is already active there's a stuck note,
1522                         // finish the old note rectangle
1523                         if (_active_notes[note->note()]) {
1524                                 CanvasNote* const old_rect = _active_notes[note->note()];
1525                                 boost::shared_ptr<NoteType> old_note = old_rect->note();
1526                                 old_rect->property_x2() = x;
1527                                 old_rect->property_outline_what() = (guint32) 0xF;
1528                         }
1529                         _active_notes[note->note()] = ev;
1530                 }
1531                 /* outline all but right edge */
1532                 ev->property_outline_what() = (guint32) (0x1 & 0x4 & 0x8);
1533         } else {
1534                 /* outline all edges */
1535                 ev->property_outline_what() = (guint32) 0xF;
1536         }
1537
1538         if (update_ghost_regions) {
1539                 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1540                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1541                         if (gr) {
1542                                 gr->update_note (ev);
1543                         }
1544                 }
1545         }
1546 }
1547
1548 double
1549 MidiRegionView::update_hit (CanvasHit* ev)
1550 {
1551         boost::shared_ptr<NoteType> note = ev->note();
1552
1553         const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1554         const double x = trackview.editor().frame_to_pixel(note_start_frames);
1555         const double diamond_size = midi_stream_view()->note_height() / 2.0;
1556         const double y = midi_stream_view()->note_to_y(note->note()) + ((diamond_size-2) / 4.0);
1557
1558         ev->move_to (x, y);
1559
1560         return diamond_size;
1561 }
1562
1563 /** Add a MIDI note to the view (with length).
1564  *
1565  * If in sustained mode, notes with length 0 will be considered active
1566  * notes, and resolve_note should be called when the corresponding note off
1567  * event arrives, to properly display the note.
1568  */
1569 void
1570 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1571 {
1572         CanvasNoteEvent* event = 0;
1573
1574         assert(note->time() >= 0);
1575         assert(midi_view()->note_mode() == Sustained || midi_view()->note_mode() == Percussive);
1576
1577         //ArdourCanvas::Group* const group = (ArdourCanvas::Group*) get_canvas_group();
1578
1579         if (midi_view()->note_mode() == Sustained) {
1580
1581                 CanvasNote* ev_rect = new CanvasNote(*this, *_note_group, note);
1582
1583                 update_note (ev_rect);
1584
1585                 event = ev_rect;
1586
1587                 MidiGhostRegion* gr;
1588
1589                 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1590                         if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1591                                 gr->add_note(ev_rect);
1592                         }
1593                 }
1594
1595         } else if (midi_view()->note_mode() == Percussive) {
1596
1597                 const double diamond_size = midi_stream_view()->note_height() / 2.0;
1598
1599                 CanvasHit* ev_diamond = new CanvasHit (*this, *_note_group, diamond_size, note);
1600
1601                 update_hit (ev_diamond);
1602
1603                 event = ev_diamond;
1604
1605         } else {
1606                 event = 0;
1607         }
1608
1609         if (event) {
1610                 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1611                         note_selected(event, true);
1612                 }
1613
1614                 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1615                         event->show_velocity();
1616                 }
1617
1618                 event->on_channel_selection_change(_last_channel_selection);
1619                 _events.push_back(event);
1620
1621                 if (visible) {
1622                         event->show();
1623                 } else {
1624                         event->hide ();
1625                 }
1626         }
1627
1628         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1629         MidiStreamView* const view = mtv->midi_view();
1630
1631         view->update_note_range (note->note());
1632 }
1633
1634 void
1635 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1636                                Evoral::MusicalTime pos, Evoral::MusicalTime len)
1637 {
1638         boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1639
1640         /* potentially extend region to hold new note */
1641
1642         framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1643         framepos_t region_end = _region->last_frame();
1644
1645         if (end_frame > region_end) {
1646                 _region->set_length (end_frame - _region->position());
1647         }
1648
1649         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1650         MidiStreamView* const view = mtv->midi_view();
1651
1652         view->update_note_range(new_note->note());
1653
1654         _marked_for_selection.clear ();
1655         clear_selection ();
1656
1657         start_note_diff_command (_("step add"));
1658         note_diff_add_note (new_note, true, false);
1659         apply_diff();
1660
1661         // last_step_edit_note = new_note;
1662 }
1663
1664 void
1665 MidiRegionView::step_sustain (Evoral::MusicalTime beats)
1666 {
1667         change_note_lengths (false, false, beats, false, true);
1668 }
1669
1670 void
1671 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext)
1672 {
1673         assert (patch->time() >= 0);
1674
1675         const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (patch->time()));
1676
1677         double const height = midi_stream_view()->contents_height();
1678
1679         boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1680                 new CanvasPatchChange(*this, *_note_group,
1681                                       displaytext,
1682                                       height,
1683                                       x, 1.0,
1684                                       _model_name,
1685                                       _custom_device_mode,
1686                                       patch)
1687                           );
1688
1689         // Show unless patch change is beyond the region bounds
1690         if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1691                 patch_change->hide();
1692         } else {
1693                 patch_change->show();
1694         }
1695
1696         _patch_changes.push_back (patch_change);
1697 }
1698
1699 void
1700 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1701 {
1702         MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1703         while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1704                 ++i;
1705         }
1706
1707         if (i != _model->patch_changes().end()) {
1708                 key.msb = (*i)->bank_msb ();
1709                 key.lsb = (*i)->bank_lsb ();
1710                 key.program_number = (*i)->program ();
1711         } else {
1712                 key.msb = key.lsb = key.program_number = 0;
1713         }
1714
1715         assert (key.is_sane());
1716 }
1717
1718
1719 void
1720 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1721 {
1722         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1723
1724         if (pc.patch()->program() != new_patch.program_number) {
1725                 c->change_program (pc.patch (), new_patch.program_number);
1726         }
1727
1728         int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1729         if (pc.patch()->bank() != new_bank) {
1730                 c->change_bank (pc.patch (), new_bank);
1731         }
1732
1733         _model->apply_command (*trackview.session(), c);
1734
1735         _patch_changes.clear ();
1736         display_patch_changes ();
1737 }
1738
1739 void
1740 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1741 {
1742         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1743
1744         if (old_change->time() != new_change.time()) {
1745                 c->change_time (old_change, new_change.time());
1746         }
1747
1748         if (old_change->channel() != new_change.channel()) {
1749                 c->change_channel (old_change, new_change.channel());
1750         }
1751
1752         if (old_change->program() != new_change.program()) {
1753                 c->change_program (old_change, new_change.program());
1754         }
1755
1756         if (old_change->bank() != new_change.bank()) {
1757                 c->change_bank (old_change, new_change.bank());
1758         }
1759
1760         _model->apply_command (*trackview.session(), c);
1761
1762         _patch_changes.clear ();
1763         display_patch_changes ();
1764 }
1765
1766 /** Add a patch change to the region.
1767  *  @param t Time in frames relative to region position
1768  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1769  *  MidiTimeAxisView::get_channel_for_add())
1770  */
1771 void
1772 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1773 {
1774         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1775
1776         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1777         c->add (MidiModel::PatchChangePtr (
1778                         new Evoral::PatchChange<Evoral::MusicalTime> (
1779                                 absolute_frames_to_source_beats (_region->position() + t),
1780                                 mtv->get_channel_for_add(), patch.program(), patch.bank()
1781                                 )
1782                         )
1783                 );
1784
1785         _model->apply_command (*trackview.session(), c);
1786
1787         _patch_changes.clear ();
1788         display_patch_changes ();
1789 }
1790
1791 void
1792 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1793 {
1794         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1795         c->change_time (pc.patch (), t);
1796         _model->apply_command (*trackview.session(), c);
1797
1798         _patch_changes.clear ();
1799         display_patch_changes ();
1800 }
1801
1802 void
1803 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1804 {
1805         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1806         c->remove (pc->patch ());
1807         _model->apply_command (*trackview.session(), c);
1808
1809         _patch_changes.clear ();
1810         display_patch_changes ();
1811 }
1812
1813 void
1814 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1815 {
1816         if (patch.patch()->program() < 127) {
1817                 MIDI::Name::PatchPrimaryKey key;
1818                 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1819                 key.program_number++;
1820                 change_patch_change (patch, key);
1821         }
1822 }
1823
1824 void
1825 MidiRegionView::next_patch (CanvasPatchChange& patch)
1826 {
1827         if (patch.patch()->program() > 0) {
1828                 MIDI::Name::PatchPrimaryKey key;
1829                 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1830                 key.program_number--;
1831                 change_patch_change (patch, key);
1832         }
1833 }
1834
1835 void
1836 MidiRegionView::previous_bank (CanvasPatchChange& patch)
1837 {
1838         if (patch.patch()->program() < 127) {
1839                 MIDI::Name::PatchPrimaryKey key;
1840                 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1841                 if (key.lsb > 0) {
1842                         key.lsb--;
1843                         change_patch_change (patch, key);
1844                 } else {
1845                         if (key.msb > 0) {
1846                                 key.lsb = 127;
1847                                 key.msb--;
1848                                 change_patch_change (patch, key);
1849                         }
1850                 }
1851         }
1852 }
1853
1854 void
1855 MidiRegionView::next_bank (CanvasPatchChange& patch)
1856 {
1857         if (patch.patch()->program() > 0) {
1858                 MIDI::Name::PatchPrimaryKey key;
1859                 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1860                 if (key.lsb < 127) {
1861                         key.lsb++;
1862                         change_patch_change (patch, key);
1863                 } else {
1864                         if (key.msb < 127) {
1865                                 key.lsb = 0;
1866                                 key.msb++;
1867                                 change_patch_change (patch, key);
1868                         }
1869                 }
1870         }
1871 }
1872
1873 void
1874 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1875 {
1876         if (_selection.empty()) {
1877                 return;
1878         }
1879
1880         _selection.erase (cne);
1881 }
1882
1883 void
1884 MidiRegionView::delete_selection()
1885 {
1886         if (_selection.empty()) {
1887                 return;
1888         }
1889
1890         start_note_diff_command (_("delete selection"));
1891
1892         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1893                 if ((*i)->selected()) {
1894                         _note_diff_command->remove((*i)->note());
1895                 }
1896         }
1897
1898         _selection.clear();
1899
1900         apply_diff ();
1901 }
1902
1903 void
1904 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1905 {
1906         start_note_diff_command (_("delete note"));
1907         _note_diff_command->remove (n);
1908         apply_diff ();
1909
1910         trackview.editor().verbose_cursor()->hide ();
1911 }
1912
1913 void
1914 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
1915 {
1916         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1917                 if ((*i) != ev) {
1918                         Selection::iterator tmp = i;
1919                         ++tmp;
1920
1921                         (*i)->set_selected (false);
1922                         (*i)->hide_velocity ();
1923                         _selection.erase (i);
1924                         
1925                         i = tmp;
1926                 } else {
1927                         ++i;
1928                 }
1929         }
1930
1931         /* this does not change the status of this regionview w.r.t the editor
1932            selection.
1933         */
1934
1935         if (signal) {
1936                 SelectionCleared (this); /* EMIT SIGNAL */
1937         }
1938 }
1939
1940 void
1941 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1942 {
1943         clear_selection_except (ev);
1944
1945         /* don't bother with checking to see if we should remove this
1946            regionview from the editor selection, since we're about to add
1947            another note, and thus put/keep this regionview in the editor
1948            selection anyway.
1949         */
1950
1951         if (!ev->selected()) {
1952                 add_to_selection (ev);
1953         }
1954 }
1955
1956 void
1957 MidiRegionView::select_all_notes ()
1958 {
1959         clear_selection ();
1960
1961         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1962                 add_to_selection (*i);
1963         }
1964 }
1965
1966 void
1967 MidiRegionView::select_range (framepos_t start, framepos_t end)
1968 {
1969         clear_selection ();
1970
1971         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1972                 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
1973                 if (t >= start && t <= end) {
1974                         add_to_selection (*i);
1975                 }
1976         }
1977 }
1978
1979 void
1980 MidiRegionView::invert_selection ()
1981 {
1982         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1983                 if ((*i)->selected()) {
1984                         remove_from_selection(*i);
1985                 } else {
1986                         add_to_selection (*i);
1987                 }
1988         }
1989 }
1990
1991 void
1992 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
1993 {
1994         uint8_t low_note = 127;
1995         uint8_t high_note = 0;
1996         MidiModel::Notes& notes (_model->notes());
1997         _optimization_iterator = _events.begin();
1998
1999         if (!add) {
2000                 clear_selection ();
2001         }
2002
2003         if (extend && _selection.empty()) {
2004                 extend = false;
2005         }
2006
2007         if (extend) {
2008
2009                 /* scan existing selection to get note range */
2010
2011                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2012                         if ((*i)->note()->note() < low_note) {
2013                                 low_note = (*i)->note()->note();
2014                         }
2015                         if ((*i)->note()->note() > high_note) {
2016                                 high_note = (*i)->note()->note();
2017                         }
2018                 }
2019
2020                 low_note = min (low_note, notenum);
2021                 high_note = max (high_note, notenum);
2022         }
2023
2024         _no_sound_notes = true;
2025
2026         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2027
2028                 boost::shared_ptr<NoteType> note (*n);
2029                 CanvasNoteEvent* cne;
2030                 bool select = false;
2031
2032                 if (((1 << note->channel()) & channel_mask) != 0) {
2033                         if (extend) {
2034                                 if ((note->note() >= low_note && note->note() <= high_note)) {
2035                                         select = true;
2036                                 }
2037                         } else if (note->note() == notenum) {
2038                                 select = true;
2039                         }
2040                 }
2041
2042                 if (select) {
2043                         if ((cne = find_canvas_note (note)) != 0) {
2044                                 // extend is false because we've taken care of it,
2045                                 // since it extends by time range, not pitch.
2046                                 note_selected (cne, add, false);
2047                         }
2048                 }
2049
2050                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2051
2052         }
2053
2054         _no_sound_notes = false;
2055 }
2056
2057 void
2058 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2059 {
2060         MidiModel::Notes& notes (_model->notes());
2061         _optimization_iterator = _events.begin();
2062
2063         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2064
2065                 boost::shared_ptr<NoteType> note (*n);
2066                 CanvasNoteEvent* cne;
2067
2068                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2069                         if ((cne = find_canvas_note (note)) != 0) {
2070                                 if (cne->selected()) {
2071                                         note_deselected (cne);
2072                                 } else {
2073                                         note_selected (cne, true, false);
2074                                 }
2075                         }
2076                 }
2077         }
2078 }
2079
2080 void
2081 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2082 {
2083         if (!add) {
2084                 clear_selection_except (ev);
2085                 if (!_selection.empty()) {
2086                         PublicEditor& editor (trackview.editor());
2087                         editor.get_selection().add (this);
2088                 }
2089         }
2090
2091         if (!extend) {
2092
2093                 if (!ev->selected()) {
2094                         add_to_selection (ev);
2095                 }
2096
2097         } else {
2098                 /* find end of latest note selected, select all between that and the start of "ev" */
2099
2100                 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2101                 Evoral::MusicalTime latest = 0;
2102
2103                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2104                         if ((*i)->note()->end_time() > latest) {
2105                                 latest = (*i)->note()->end_time();
2106                         }
2107                         if ((*i)->note()->time() < earliest) {
2108                                 earliest = (*i)->note()->time();
2109                         }
2110                 }
2111
2112                 if (ev->note()->end_time() > latest) {
2113                         latest = ev->note()->end_time();
2114                 }
2115
2116                 if (ev->note()->time() < earliest) {
2117                         earliest = ev->note()->time();
2118                 }
2119
2120                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2121
2122                         /* find notes entirely within OR spanning the earliest..latest range */
2123
2124                         if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2125                             ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2126                                 add_to_selection (*i);
2127                         }
2128
2129                 }
2130         }
2131 }
2132
2133 void
2134 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2135 {
2136         remove_from_selection (ev);
2137 }
2138
2139 void
2140 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2141 {
2142         if (x1 > x2) {
2143                 swap (x1, x2);
2144         }
2145
2146         if (y1 > y2) {
2147                 swap (y1, y2);
2148         }
2149
2150         // TODO: Make this faster by storing the last updated selection rect, and only
2151         // adjusting things that are in the area that appears/disappeared.
2152         // We probably need a tree to be able to find events in O(log(n)) time.
2153
2154         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2155
2156                 /* check if any corner of the note is inside the rect
2157
2158                    Notes:
2159                    1) this is computing "touched by", not "contained by" the rect.
2160                    2) this does not require that events be sorted in time.
2161                 */
2162
2163                 const double ix1 = (*i)->x1();
2164                 const double ix2 = (*i)->x2();
2165                 const double iy1 = (*i)->y1();
2166                 const double iy2 = (*i)->y2();
2167
2168                 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2169                     (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2170                     (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2171                     (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2172
2173                         // Inside rectangle
2174                         if (!(*i)->selected()) {
2175                                 add_to_selection (*i);
2176                         }
2177                 } else if ((*i)->selected() && !extend) {
2178                         // Not inside rectangle
2179                         remove_from_selection (*i);
2180                 }
2181         }
2182 }
2183
2184 void
2185 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2186 {
2187         Selection::iterator i = _selection.find (ev);
2188
2189         if (i != _selection.end()) {
2190                 _selection.erase (i);
2191         }
2192
2193         ev->set_selected (false);
2194         ev->hide_velocity ();
2195
2196         if (_selection.empty()) {
2197                 PublicEditor& editor (trackview.editor());
2198                 editor.get_selection().remove (this);
2199         }
2200 }
2201
2202 void
2203 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2204 {
2205         bool add_mrv_selection = false;
2206
2207         if (_selection.empty()) {
2208                 add_mrv_selection = true;
2209         }
2210
2211         if (_selection.insert (ev).second) {
2212                 ev->set_selected (true);
2213                 play_midi_note ((ev)->note());
2214         }
2215
2216         if (add_mrv_selection) {
2217                 PublicEditor& editor (trackview.editor());
2218                 editor.get_selection().add (this);
2219         }
2220 }
2221
2222 void
2223 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2224 {
2225         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2226         PossibleChord to_play;
2227         Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2228
2229         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2230                 if ((*i)->note()->time() < earliest) {
2231                         earliest = (*i)->note()->time();
2232                 }
2233         }
2234
2235         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2236                 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2237                         to_play.push_back ((*i)->note());
2238                 }
2239                 (*i)->move_event(dx, dy);
2240         }
2241
2242         if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2243
2244                 if (to_play.size() > 1) {
2245
2246                         PossibleChord shifted;
2247
2248                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2249                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2250                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2251                                 shifted.push_back (moved_note);
2252                         }
2253
2254                         play_midi_chord (shifted);
2255
2256                 } else if (!to_play.empty()) {
2257
2258                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2259                         moved_note->set_note (moved_note->note() + cumulative_dy);
2260                         play_midi_note (moved_note);
2261                 }
2262         }
2263 }
2264
2265 void
2266 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2267 {
2268         assert (!_selection.empty());
2269
2270         uint8_t lowest_note_in_selection  = 127;
2271         uint8_t highest_note_in_selection = 0;
2272         uint8_t highest_note_difference = 0;
2273
2274         // find highest and lowest notes first
2275
2276         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2277                 uint8_t pitch = (*i)->note()->note();
2278                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2279                 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2280         }
2281
2282         /*
2283           cerr << "dnote: " << (int) dnote << endl;
2284           cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2285           << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2286           cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2287           << int(highest_note_in_selection) << endl;
2288           cerr << "selection size: " << _selection.size() << endl;
2289           cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2290         */
2291
2292         // Make sure the note pitch does not exceed the MIDI standard range
2293         if (highest_note_in_selection + dnote > 127) {
2294                 highest_note_difference = highest_note_in_selection - 127;
2295         }
2296
2297         start_note_diff_command (_("move notes"));
2298
2299         for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2300
2301                 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (source_beats_to_absolute_frames ((*i)->note()->time()) + dt);
2302
2303                 if (new_time < 0) {
2304                         continue;
2305                 }
2306
2307                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2308
2309                 uint8_t original_pitch = (*i)->note()->note();
2310                 uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2311
2312                 // keep notes in standard midi range
2313                 clamp_to_0_127(new_pitch);
2314
2315                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2316                 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2317
2318                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2319         }
2320
2321         apply_diff();
2322
2323         // care about notes being moved beyond the upper/lower bounds on the canvas
2324         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
2325             highest_note_in_selection > midi_stream_view()->highest_note()) {
2326                 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2327         }
2328 }
2329
2330 /** @param x Pixel relative to the region position.
2331  *  @return Snapped frame relative to the region position.
2332  */
2333 framepos_t
2334 MidiRegionView::snap_pixel_to_frame(double x)
2335 {
2336         PublicEditor& editor (trackview.editor());
2337         return snap_frame_to_frame (editor.pixel_to_frame (x));
2338 }
2339
2340 /** @param x Pixel relative to the region position.
2341  *  @return Snapped pixel relative to the region position.
2342  */
2343 double
2344 MidiRegionView::snap_to_pixel(double x)
2345 {
2346         return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2347 }
2348
2349 double
2350 MidiRegionView::get_position_pixels()
2351 {
2352         framepos_t region_frame = get_position();
2353         return trackview.editor().frame_to_pixel(region_frame);
2354 }
2355
2356 double
2357 MidiRegionView::get_end_position_pixels()
2358 {
2359         framepos_t frame = get_position() + get_duration ();
2360         return trackview.editor().frame_to_pixel(frame);
2361 }
2362
2363 framepos_t
2364 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2365 {
2366         /* the time converter will return the frame corresponding to `beats'
2367            relative to the start of the source. The start of the source
2368            is an implied position given by region->position - region->start
2369         */
2370         const framepos_t source_start = _region->position() - _region->start();
2371         return  source_start +  _source_relative_time_converter.to (beats);
2372 }
2373
2374 double
2375 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2376 {
2377         /* the `frames' argument needs to be converted into a frame count
2378            relative to the start of the source before being passed in to the
2379            converter.
2380         */
2381         const framepos_t source_start = _region->position() - _region->start();
2382         return  _source_relative_time_converter.from (frames - source_start);
2383 }
2384
2385 framepos_t
2386 MidiRegionView::region_beats_to_region_frames(double beats) const
2387 {
2388         return _region_relative_time_converter.to(beats);
2389 }
2390
2391 double
2392 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2393 {
2394         return _region_relative_time_converter.from(frames);
2395 }
2396
2397 void
2398 MidiRegionView::begin_resizing (bool /*at_front*/)
2399 {
2400         _resize_data.clear();
2401
2402         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2403                 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2404
2405                 // only insert CanvasNotes into the map
2406                 if (note) {
2407                         NoteResizeData *resize_data = new NoteResizeData();
2408                         resize_data->canvas_note = note;
2409
2410                         // create a new SimpleRect from the note which will be the resize preview
2411                         SimpleRect *resize_rect = new SimpleRect(
2412                                 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2413
2414                         // calculate the colors: get the color settings
2415                         uint32_t fill_color = UINT_RGBA_CHANGE_A(
2416                                 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2417                                 128);
2418
2419                         // make the resize preview notes more transparent and bright
2420                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2421
2422                         // calculate color based on note velocity
2423                         resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2424                                 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2425                                 fill_color,
2426                                 0.85);
2427
2428                         resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2429                                 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2430
2431                         resize_data->resize_rect = resize_rect;
2432                         _resize_data.push_back(resize_data);
2433                 }
2434         }
2435 }
2436
2437 /** Update resizing notes while user drags.
2438  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2439  * @param at_front which end of the note (true == note on, false == note off)
2440  * @param delta_x change in mouse position since the start of the drag
2441  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2442  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2443  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2444  * as the \a primary note.
2445  */
2446 void
2447 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2448 {
2449         bool cursor_set = false;
2450
2451         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2452                 SimpleRect* resize_rect = (*i)->resize_rect;
2453                 CanvasNote* canvas_note = (*i)->canvas_note;
2454                 double current_x;
2455
2456                 if (at_front) {
2457                         if (relative) {
2458                                 current_x = canvas_note->x1() + delta_x;
2459                         } else {
2460                                 current_x = primary->x1() + delta_x;
2461                         }
2462                 } else {
2463                         if (relative) {
2464                                 current_x = canvas_note->x2() + delta_x;
2465                         } else {
2466                                 current_x = primary->x2() + delta_x;
2467                         }
2468                 }
2469
2470                 if (at_front) {
2471                         resize_rect->property_x1() = snap_to_pixel(current_x);
2472                         resize_rect->property_x2() = canvas_note->x2();
2473                 } else {
2474                         resize_rect->property_x2() = snap_to_pixel(current_x);
2475                         resize_rect->property_x1() = canvas_note->x1();
2476                 }
2477
2478                 if (!cursor_set) {
2479                         double beats;
2480
2481                         beats = snap_pixel_to_frame (current_x);
2482                         beats = region_frames_to_region_beats (beats);
2483
2484                         double len;
2485
2486                         if (at_front) {
2487                                 if (beats < canvas_note->note()->end_time()) {
2488                                         len = canvas_note->note()->time() - beats;
2489                                         len += canvas_note->note()->length();
2490                                 } else {
2491                                         len = 0;
2492                                 }
2493                         } else {
2494                                 if (beats >= canvas_note->note()->time()) {
2495                                         len = beats - canvas_note->note()->time();
2496                                 } else {
2497                                         len = 0;
2498                                 }
2499                         }
2500
2501                         char buf[16];
2502                         snprintf (buf, sizeof (buf), "%.3g beats", len);
2503                         show_verbose_cursor (buf, 0, 0);
2504
2505                         cursor_set = true;
2506                 }
2507
2508         }
2509 }
2510
2511
2512 /** Finish resizing notes when the user releases the mouse button.
2513  *  Parameters the same as for \a update_resizing().
2514  */
2515 void
2516 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2517 {
2518         start_note_diff_command (_("resize notes"));
2519
2520         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2521                 CanvasNote*  canvas_note = (*i)->canvas_note;
2522                 SimpleRect*  resize_rect = (*i)->resize_rect;
2523
2524                 /* Get the new x position for this resize, which is in pixels relative
2525                  * to the region position.
2526                  */
2527                 
2528                 double current_x;
2529
2530                 if (at_front) {
2531                         if (relative) {
2532                                 current_x = canvas_note->x1() + delta_x;
2533                         } else {
2534                                 current_x = primary->x1() + delta_x;
2535                         }
2536                 } else {
2537                         if (relative) {
2538                                 current_x = canvas_note->x2() + delta_x;
2539                         } else {
2540                                 current_x = primary->x2() + delta_x;
2541                         }
2542                 }
2543
2544                 /* Convert that to a frame within the source */
2545                 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2546
2547                 /* and then to beats */
2548                 current_x = region_frames_to_region_beats (current_x);
2549
2550                 if (at_front && current_x < canvas_note->note()->end_time()) {
2551                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2552
2553                         double len = canvas_note->note()->time() - current_x;
2554                         len += canvas_note->note()->length();
2555
2556                         if (len > 0) {
2557                                 /* XXX convert to beats */
2558                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2559                         }
2560                 }
2561
2562                 if (!at_front) {
2563                         double len = current_x - canvas_note->note()->time();
2564
2565                         if (len > 0) {
2566                                 /* XXX convert to beats */
2567                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2568                         }
2569                 }
2570
2571                 delete resize_rect;
2572                 delete (*i);
2573         }
2574
2575         _resize_data.clear();
2576         apply_diff();
2577 }
2578
2579 void
2580 MidiRegionView::abort_resizing ()
2581 {
2582         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2583                 delete (*i)->resize_rect;
2584                 delete *i;
2585         }
2586
2587         _resize_data.clear ();
2588 }
2589
2590 void
2591 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2592 {
2593         uint8_t new_velocity;
2594
2595         if (relative) {
2596                 new_velocity = event->note()->velocity() + velocity;
2597                 clamp_to_0_127(new_velocity);
2598         } else {
2599                 new_velocity = velocity;
2600         }
2601
2602         event->set_selected (event->selected()); // change color
2603
2604         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2605 }
2606
2607 void
2608 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2609 {
2610         uint8_t new_note;
2611
2612         if (relative) {
2613                 new_note = event->note()->note() + note;
2614         } else {
2615                 new_note = note;
2616         }
2617
2618         clamp_to_0_127 (new_note);
2619         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2620 }
2621
2622 void
2623 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2624 {
2625         bool change_start = false;
2626         bool change_length = false;
2627         Evoral::MusicalTime new_start = 0;
2628         Evoral::MusicalTime new_length = 0;
2629
2630         /* NOTE: the semantics of the two delta arguments are slightly subtle:
2631
2632            front_delta: if positive - move the start of the note later in time (shortening it)
2633            if negative - move the start of the note earlier in time (lengthening it)
2634
2635            end_delta:   if positive - move the end of the note later in time (lengthening it)
2636            if negative - move the end of the note earlier in time (shortening it)
2637         */
2638
2639         if (front_delta) {
2640                 if (front_delta < 0) {
2641
2642                         if (event->note()->time() < -front_delta) {
2643                                 new_start = 0;
2644                         } else {
2645                                 new_start = event->note()->time() + front_delta; // moves earlier
2646                         }
2647
2648                         /* start moved toward zero, so move the end point out to where it used to be.
2649                            Note that front_delta is negative, so this increases the length.
2650                         */
2651
2652                         new_length = event->note()->length() - front_delta;
2653                         change_start = true;
2654                         change_length = true;
2655
2656                 } else {
2657
2658                         Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2659
2660                         if (new_pos < event->note()->end_time()) {
2661                                 new_start = event->note()->time() + front_delta;
2662                                 /* start moved toward the end, so move the end point back to where it used to be */
2663                                 new_length = event->note()->length() - front_delta;
2664                                 change_start = true;
2665                                 change_length = true;
2666                         }
2667                 }
2668
2669         }
2670
2671         if (end_delta) {
2672                 bool can_change = true;
2673                 if (end_delta < 0) {
2674                         if (event->note()->length() < -end_delta) {
2675                                 can_change = false;
2676                         }
2677                 }
2678
2679                 if (can_change) {
2680                         new_length = event->note()->length() + end_delta;
2681                         change_length = true;
2682                 }
2683         }
2684
2685         if (change_start) {
2686                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2687         }
2688
2689         if (change_length) {
2690                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2691         }
2692 }
2693
2694 void
2695 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2696 {
2697         uint8_t new_channel;
2698
2699         if (relative) {
2700                 if (chn < 0.0) {
2701                         if (event->note()->channel() < -chn) {
2702                                 new_channel = 0;
2703                         } else {
2704                                 new_channel = event->note()->channel() + chn;
2705                         }
2706                 } else {
2707                         new_channel = event->note()->channel() + chn;
2708                 }
2709         } else {
2710                 new_channel = (uint8_t) chn;
2711         }
2712
2713         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2714 }
2715
2716 void
2717 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2718 {
2719         Evoral::MusicalTime new_time;
2720
2721         if (relative) {
2722                 if (delta < 0.0) {
2723                         if (event->note()->time() < -delta) {
2724                                 new_time = 0;
2725                         } else {
2726                                 new_time = event->note()->time() + delta;
2727                         }
2728                 } else {
2729                         new_time = event->note()->time() + delta;
2730                 }
2731         } else {
2732                 new_time = delta;
2733         }
2734
2735         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2736 }
2737
2738 void
2739 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2740 {
2741         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2742 }
2743
2744 void
2745 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2746 {
2747         int8_t delta;
2748
2749         if (_selection.empty()) {
2750                 return;
2751         }
2752
2753         if (fine) {
2754                 delta = 1;
2755         } else {
2756                 delta = 10;
2757         }
2758
2759         if (!up) {
2760                 delta = -delta;
2761         }
2762
2763         if (!allow_smush) {
2764                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2765                         if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2766                                 return;
2767                         }
2768                 }
2769         }
2770
2771         start_note_diff_command (_("change velocities"));
2772
2773         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2774                 Selection::iterator next = i;
2775                 ++next;
2776                 change_note_velocity (*i, delta, true);
2777                 i = next;
2778         }
2779
2780         apply_diff();
2781
2782         if (!_selection.empty()) {
2783                 char buf[24];
2784                 snprintf (buf, sizeof (buf), "Vel %d",
2785                           (int) (*_selection.begin())->note()->velocity());
2786                 show_verbose_cursor (buf, 10, 10);
2787         }
2788 }
2789
2790
2791 void
2792 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2793 {
2794         if (_selection.empty()) {
2795                 return;
2796         }
2797
2798         int8_t delta;
2799
2800         if (fine) {
2801                 delta = 1;
2802         } else {
2803                 delta = 12;
2804         }
2805
2806         if (!up) {
2807                 delta = -delta;
2808         }
2809
2810         if (!allow_smush) {
2811                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2812                         if (!up) {
2813                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2814                                         return;
2815                                 }
2816                         } else {
2817                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
2818                                         return;
2819                                 }
2820                         }
2821                 }
2822         }
2823
2824         start_note_diff_command (_("transpose"));
2825
2826         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2827                 Selection::iterator next = i;
2828                 ++next;
2829                 change_note_note (*i, delta, true);
2830                 i = next;
2831         }
2832
2833         apply_diff ();
2834 }
2835
2836 void
2837 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2838 {
2839         if (delta == 0.0) {
2840                 if (fine) {
2841                         delta = 1.0/128.0;
2842                 } else {
2843                         /* grab the current grid distance */
2844                         bool success;
2845                         delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2846                         if (!success) {
2847                                 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2848                                 cerr << "Grid type not available as beats - TO BE FIXED\n";
2849                                 return;
2850                         }
2851                 }
2852         }
2853
2854         if (shorter) {
2855                 delta = -delta;
2856         }
2857
2858         start_note_diff_command (_("change note lengths"));
2859
2860         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2861                 Selection::iterator next = i;
2862                 ++next;
2863
2864                 /* note the negation of the delta for start */
2865
2866                 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2867                 i = next;
2868         }
2869
2870         apply_diff ();
2871
2872 }
2873
2874 void
2875 MidiRegionView::nudge_notes (bool forward)
2876 {
2877         if (_selection.empty()) {
2878                 return;
2879         }
2880
2881         /* pick a note as the point along the timeline to get the nudge distance.
2882            its not necessarily the earliest note, so we may want to pull the notes out
2883            into a vector and sort before using the first one.
2884         */
2885
2886         framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
2887         framepos_t unused;
2888         framepos_t distance;
2889
2890         if (trackview.editor().snap_mode() == Editing::SnapOff) {
2891
2892                 /* grid is off - use nudge distance */
2893
2894                 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2895
2896         } else {
2897
2898                 /* use grid */
2899
2900                 framepos_t next_pos = ref_point;
2901
2902                 if (forward) {
2903                         if (max_framepos - 1 < next_pos) {
2904                                 next_pos += 1;
2905                         }
2906                 } else {
2907                         if (next_pos == 0) {
2908                                 return;
2909                         }
2910                         next_pos -= 1;
2911                 }
2912
2913                 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2914                 distance = ref_point - next_pos;
2915         }
2916
2917         if (distance == 0) {
2918                 return;
2919         }
2920
2921         Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
2922
2923         if (!forward) {
2924                 delta = -delta;
2925         }
2926
2927         start_note_diff_command (_("nudge"));
2928
2929         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2930                 Selection::iterator next = i;
2931                 ++next;
2932                 change_note_time (*i, delta, true);
2933                 i = next;
2934         }
2935
2936         apply_diff ();
2937 }
2938
2939 void
2940 MidiRegionView::change_channel(uint8_t channel)
2941 {
2942         start_note_diff_command(_("change channel"));
2943         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2944                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2945         }
2946
2947         apply_diff();
2948 }
2949
2950
2951 void
2952 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2953 {
2954         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2955
2956         _pre_enter_cursor = editor->get_canvas_cursor ();
2957
2958         if (_mouse_state == SelectTouchDragging) {
2959                 note_selected (ev, true);
2960         }
2961
2962         show_verbose_cursor (ev->note ());
2963 }
2964
2965 void
2966 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2967 {
2968         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2969
2970         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2971                 (*i)->hide_velocity ();
2972         }
2973
2974         editor->verbose_cursor()->hide ();
2975
2976         if (_pre_enter_cursor) {
2977                 editor->set_canvas_cursor (_pre_enter_cursor);
2978                 _pre_enter_cursor = 0;
2979         }
2980 }
2981
2982 void
2983 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2984 {
2985         ostringstream s;
2986         /* XXX should get patch name if we can */
2987         s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->patch()->channel() + 1);
2988         show_verbose_cursor (s.str(), 10, 20);
2989 }
2990
2991 void
2992 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
2993 {
2994         trackview.editor().verbose_cursor()->hide ();
2995 }
2996
2997 void
2998 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
2999 {
3000         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3001
3002         if (x_fraction > 0.0 && x_fraction < 0.25) {
3003                 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3004         } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
3005                 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3006         } else {
3007                 if (_pre_enter_cursor && can_set_cursor) {
3008                         editor->set_canvas_cursor (_pre_enter_cursor);
3009                 }
3010         }
3011 }
3012
3013 void
3014 MidiRegionView::set_frame_color()
3015 {
3016         uint32_t f;
3017
3018         TimeAxisViewItem::set_frame_color ();
3019
3020         if (!frame) {
3021                 return;
3022         }
3023
3024         if (_selected) {
3025                 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3026         } else if (high_enough_for_name) {
3027                 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3028         } else {
3029                 f = fill_color;
3030         }
3031
3032         if (!rect_visible) {
3033                 f = UINT_RGBA_CHANGE_A (f, 0);
3034         }
3035
3036         frame->property_fill_color_rgba() = f;
3037 }
3038
3039 void
3040 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3041 {
3042         switch (mode) {
3043         case AllChannels:
3044         case FilterChannels:
3045                 _force_channel = -1;
3046                 break;
3047         case ForceChannel:
3048                 _force_channel = mask;
3049                 mask = 0xFFFF; // Show all notes as active (below)
3050         };
3051
3052         // Update notes for selection
3053         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3054                 (*i)->on_channel_selection_change(mask);
3055         }
3056
3057         _last_channel_selection = mask;
3058
3059         _patch_changes.clear ();
3060         display_patch_changes ();
3061 }
3062
3063 void
3064 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
3065 {
3066         _model_name         = model;
3067         _custom_device_mode = custom_device_mode;
3068         redisplay_model();
3069 }
3070
3071 void
3072 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3073 {
3074         if (_selection.empty()) {
3075                 return;
3076         }
3077
3078         PublicEditor& editor (trackview.editor());
3079
3080         switch (op) {
3081         case Delete:
3082                 /* XXX what to do ? */
3083                 break;
3084         case Cut:
3085         case Copy:
3086                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3087                 break;
3088         default:
3089                 break;
3090         }
3091
3092         if (op != Copy) {
3093
3094                 start_note_diff_command();
3095
3096                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3097                         switch (op) {
3098                         case Copy:
3099                                 break;
3100                         case Delete:
3101                         case Cut:
3102                         case Clear:
3103                                 note_diff_remove_note (*i);
3104                                 break;
3105                         }
3106                 }
3107
3108                 apply_diff();
3109         }
3110 }
3111
3112 MidiCutBuffer*
3113 MidiRegionView::selection_as_cut_buffer () const
3114 {
3115         Notes notes;
3116
3117         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3118                 NoteType* n = (*i)->note().get();
3119                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3120         }
3121
3122         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3123         cb->set (notes);
3124
3125         return cb;
3126 }
3127
3128 /** This method handles undo */
3129 void
3130 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3131 {
3132         if (mcb.empty()) {
3133                 return;
3134         }
3135
3136         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3137
3138         trackview.session()->begin_reversible_command (_("paste"));
3139
3140         start_note_diff_command (_("paste"));
3141
3142         Evoral::MusicalTime beat_delta;
3143         Evoral::MusicalTime paste_pos_beats;
3144         Evoral::MusicalTime duration;
3145         Evoral::MusicalTime end_point = 0;
3146
3147         duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3148         paste_pos_beats = region_frames_to_region_beats (pos - _region->position());
3149         beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3150         paste_pos_beats = 0;
3151
3152         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6 ; beat delta = %7\n",
3153                                                        (*mcb.notes().begin())->time(),
3154                                                        (*mcb.notes().rbegin())->end_time(),
3155                                                        duration, pos, _region->position(),
3156                                                        paste_pos_beats, beat_delta));
3157
3158         clear_selection ();
3159
3160         for (int n = 0; n < (int) times; ++n) {
3161
3162                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3163
3164                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3165                         copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3166
3167                         /* make all newly added notes selected */
3168
3169                         note_diff_add_note (copied_note, true);
3170                         end_point = copied_note->end_time();
3171                 }
3172
3173                 paste_pos_beats += duration;
3174         }
3175
3176         /* if we pasted past the current end of the region, extend the region */
3177
3178         framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3179         framepos_t region_end = _region->position() + _region->length() - 1;
3180
3181         if (end_frame > region_end) {
3182
3183                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3184
3185                 _region->clear_changes ();
3186                 _region->set_length (end_frame);
3187                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3188         }
3189
3190         apply_diff (true);
3191
3192         trackview.session()->commit_reversible_command ();
3193 }
3194
3195 struct EventNoteTimeEarlyFirstComparator {
3196         bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3197                 return a->note()->time() < b->note()->time();
3198         }
3199 };
3200
3201 void
3202 MidiRegionView::time_sort_events ()
3203 {
3204         if (!_sort_needed) {
3205                 return;
3206         }
3207
3208         EventNoteTimeEarlyFirstComparator cmp;
3209         _events.sort (cmp);
3210
3211         _sort_needed = false;
3212 }
3213
3214 void
3215 MidiRegionView::goto_next_note (bool add_to_selection)
3216 {
3217         bool use_next = false;
3218
3219         if (_events.back()->selected()) {
3220                 return;
3221         }
3222
3223         time_sort_events ();
3224
3225         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3226         uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3227
3228         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3229                 if ((*i)->selected()) {
3230                         use_next = true;
3231                         continue;
3232                 } else if (use_next) {
3233                         if (channel_mask & (1 << (*i)->note()->channel())) {
3234                                 if (!add_to_selection) {
3235                                         unique_select (*i);
3236                                 } else {
3237                                         note_selected (*i, true, false);
3238                                 }
3239                                 return;
3240                         }
3241                 }
3242         }
3243
3244         /* use the first one */
3245
3246         if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3247                 unique_select (_events.front());
3248         }
3249 }
3250
3251 void
3252 MidiRegionView::goto_previous_note (bool add_to_selection)
3253 {
3254         bool use_next = false;
3255
3256         if (_events.front()->selected()) {
3257                 return;
3258         }
3259
3260         time_sort_events ();
3261
3262         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3263         uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3264
3265         for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3266                 if ((*i)->selected()) {
3267                         use_next = true;
3268                         continue;
3269                 } else if (use_next) {
3270                         if (channel_mask & (1 << (*i)->note()->channel())) {
3271                                 if (!add_to_selection) {
3272                                         unique_select (*i);
3273                                 } else {
3274                                         note_selected (*i, true, false);
3275                                 }
3276                                 return;
3277                         }
3278                 }
3279         }
3280
3281         /* use the last one */
3282
3283         if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3284                 unique_select (*(_events.rbegin()));
3285         }
3286 }
3287
3288 void
3289 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3290 {
3291         bool had_selected = false;
3292
3293         time_sort_events ();
3294
3295         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3296                 if ((*i)->selected()) {
3297                         selected.insert ((*i)->note());
3298                         had_selected = true;
3299                 }
3300         }
3301
3302         if (allow_all_if_none_selected && !had_selected) {
3303                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3304                         selected.insert ((*i)->note());
3305                 }
3306         }
3307 }
3308
3309 void
3310 MidiRegionView::update_ghost_note (double x, double y)
3311 {
3312         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3313
3314         _last_ghost_x = x;
3315         _last_ghost_y = y;
3316
3317         _note_group->w2i (x, y);
3318
3319         PublicEditor& editor = trackview.editor ();
3320         
3321         framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3322         framecnt_t grid_frames;
3323         framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3324         
3325         /* use region_frames... because we are converting a delta within the region
3326         */
3327          
3328         double length = region_frames_to_region_beats (snap_frame_to_frame (f + grid_frames) - f);
3329
3330         /* note that this sets the time of the ghost note in beats relative to
3331            the start of the source; that is how all note times are stored.
3332         */
3333         _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3334         _ghost_note->note()->set_length (length);
3335         _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3336         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3337
3338         /* the ghost note does not appear in ghost regions, so pass false in here */
3339         update_note (_ghost_note, false);
3340
3341         show_verbose_cursor (_ghost_note->note ());
3342 }
3343
3344 void
3345 MidiRegionView::create_ghost_note (double x, double y)
3346 {
3347         remove_ghost_note ();
3348
3349         boost::shared_ptr<NoteType> g (new NoteType);
3350         _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3351         _ghost_note->property_outline_color_rgba() = 0x000000aa;
3352         update_ghost_note (x, y);
3353         _ghost_note->show ();
3354
3355         _last_ghost_x = x;
3356         _last_ghost_y = y;
3357
3358         show_verbose_cursor (_ghost_note->note ());
3359 }
3360
3361 void
3362 MidiRegionView::snap_changed ()
3363 {
3364         if (!_ghost_note) {
3365                 return;
3366         }
3367
3368         create_ghost_note (_last_ghost_x, _last_ghost_y);
3369 }
3370
3371 void
3372 MidiRegionView::drop_down_keys ()
3373 {
3374         _mouse_state = None;
3375 }
3376
3377 void
3378 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3379 {
3380         double note = midi_stream_view()->y_to_note(y);
3381         Events e;
3382         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3383
3384         uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3385
3386         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3387                 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3388         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3389                 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3390         } else {
3391                 return;
3392         }
3393
3394         bool add_mrv_selection = false;
3395
3396         if (_selection.empty()) {
3397                 add_mrv_selection = true;
3398         }
3399
3400         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3401                 if (_selection.insert (*i).second) {
3402                         (*i)->set_selected (true);
3403                 }
3404         }
3405
3406         if (add_mrv_selection) {
3407                 PublicEditor& editor (trackview.editor());
3408                 editor.get_selection().add (this);
3409         }
3410 }
3411
3412 void
3413 MidiRegionView::color_handler ()
3414 {
3415         RegionView::color_handler ();
3416
3417         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3418                 (*i)->set_selected ((*i)->selected()); // will change color
3419         }
3420
3421         /* XXX probably more to do here */
3422 }
3423
3424 void
3425 MidiRegionView::enable_display (bool yn)
3426 {
3427         RegionView::enable_display (yn);
3428         if (yn) {
3429                 redisplay_model ();
3430         }
3431 }
3432
3433 void
3434 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3435 {
3436         if (_step_edit_cursor == 0) {
3437                 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3438
3439                 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3440                 _step_edit_cursor->property_y1() = 0;
3441                 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3442                 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3443                 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3444         }
3445
3446         move_step_edit_cursor (pos);
3447         _step_edit_cursor->show ();
3448 }
3449
3450 void
3451 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3452 {
3453         _step_edit_cursor_position = pos;
3454
3455         if (_step_edit_cursor) {
3456                 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3457                 _step_edit_cursor->property_x1() = pixel;
3458                 set_step_edit_cursor_width (_step_edit_cursor_width);
3459         }
3460 }
3461
3462 void
3463 MidiRegionView::hide_step_edit_cursor ()
3464 {
3465         if (_step_edit_cursor) {
3466                 _step_edit_cursor->hide ();
3467         }
3468 }
3469
3470 void
3471 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3472 {
3473         _step_edit_cursor_width = beats;
3474
3475         if (_step_edit_cursor) {
3476                 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3477         }
3478 }
3479
3480 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
3481  *  @param w Source that the data will end up in.
3482  */
3483 void
3484 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3485 {
3486         if (!_active_notes) {
3487                 /* we aren't actively being recorded to */
3488                 return;
3489         }
3490
3491         boost::shared_ptr<MidiSource> src = w.lock ();
3492         if (!src || src != midi_region()->midi_source()) {
3493                 /* recorded data was not destined for our source */
3494                 return;
3495         }
3496
3497         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3498
3499         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3500
3501         BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3502
3503         framepos_t back = max_framepos;
3504
3505         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3506                 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3507                 assert (ev.buffer ());
3508
3509                 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3510                    frames from the start of the source, and so time_beats is in terms of the
3511                    source.
3512                 */
3513
3514                 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3515
3516                 if (ev.type() == MIDI_CMD_NOTE_ON) {
3517
3518                         boost::shared_ptr<NoteType> note (
3519                                 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3520                                                           );
3521
3522                         add_note (note, true);
3523
3524                         /* fix up our note range */
3525                         if (ev.note() < _current_range_min) {
3526                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3527                         } else if (ev.note() > _current_range_max) {
3528                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3529                         }
3530
3531                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3532                         resolve_note (ev.note (), time_beats);
3533                 }
3534
3535                 back = ev.time ();
3536         }
3537
3538         midi_stream_view()->check_record_layers (region(), back);
3539 }
3540
3541 void
3542 MidiRegionView::trim_front_starting ()
3543 {
3544         /* Reparent the note group to the region view's parent, so that it doesn't change
3545            when the region view is trimmed.
3546         */
3547         _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3548         _temporary_note_group->move (group->property_x(), group->property_y());
3549         _note_group->reparent (*_temporary_note_group);
3550 }
3551
3552 void
3553 MidiRegionView::trim_front_ending ()
3554 {
3555         _note_group->reparent (*group);
3556         delete _temporary_note_group;
3557         _temporary_note_group = 0;
3558
3559         if (_region->start() < 0) {
3560                 /* Trim drag made start time -ve; fix this */
3561                 midi_region()->fix_negative_start ();
3562         }
3563 }
3564
3565 void
3566 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3567 {
3568         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3569         if (d.run () != Gtk::RESPONSE_ACCEPT) {
3570                 return;
3571         }
3572
3573         change_patch_change (pc->patch(), d.patch ());
3574 }
3575
3576
3577 void
3578 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3579 {
3580         char buf[24];
3581         snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3582                   Evoral::midi_note_name (n->note()).c_str(),
3583                   (int) n->note (),
3584                   (int) n->channel() + 1,
3585                   (int) n->velocity());
3586
3587         show_verbose_cursor (buf, 10, 20);
3588 }
3589
3590 void
3591 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3592 {
3593         double wx, wy;
3594
3595         trackview.editor().get_pointer_position (wx, wy);
3596
3597         wx += xoffset;
3598         wy += yoffset;
3599
3600         /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3601
3602         double x1, y1, x2, y2;
3603         trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3604
3605         if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3606                 wy -= (y2 - y1) + 2 * yoffset;
3607         }
3608
3609         trackview.editor().verbose_cursor()->set (text, wx, wy);
3610         trackview.editor().verbose_cursor()->show ();
3611 }
3612
3613 /** @param p A session framepos.
3614  *  @param grid_frames Filled in with the number of frames that a grid interval is at p.
3615  *  @return p snapped to the grid subdivision underneath it.
3616  */
3617 framepos_t
3618 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3619 {
3620         PublicEditor& editor = trackview.editor ();
3621         
3622         bool success;
3623         Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3624
3625         if (!success) {
3626                 grid_beats = 1;
3627         }
3628         
3629         grid_frames = region_beats_to_region_frames (grid_beats);
3630
3631         /* Hack so that we always snap to the note that we are over, instead of snapping
3632            to the next one if we're more than halfway through the one we're over.
3633         */
3634         if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3635                 p -= grid_frames / 2;
3636         }
3637
3638         return snap_frame_to_frame (p);
3639 }
3640
3641 /** Called when the selection has been cleared in any MidiRegionView.
3642  *  @param rv MidiRegionView that the selection was cleared in.
3643  */
3644 void
3645 MidiRegionView::selection_cleared (MidiRegionView* rv)
3646 {
3647         if (rv == this) {
3648                 return;
3649         }
3650
3651         /* Clear our selection in sympathy; but don't signal the fact */
3652         clear_selection (false);
3653 }