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