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