do not convert from grid-type-beats to frames and back to beats when determining...
[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_beat;
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         const double x = trackview.editor().frame_to_pixel (source_beats_to_region_frames (patch->time()));
1681
1682         double const height = midi_stream_view()->contents_height();
1683
1684         boost::shared_ptr<CanvasPatchChange> patch_change = boost::shared_ptr<CanvasPatchChange>(
1685                 new CanvasPatchChange(*this, *_note_group,
1686                                       displaytext,
1687                                       height,
1688                                       x, 1.0,
1689                                       _model_name,
1690                                       _custom_device_mode,
1691                                       patch)
1692                           );
1693
1694         // Show unless patch change is beyond the region bounds
1695         if (patch->time() - _region->start() >= _region->length() || patch->time() < _region->start()) {
1696                 patch_change->hide();
1697         } else {
1698                 patch_change->show();
1699         }
1700
1701         _patch_changes.push_back (patch_change);
1702 }
1703
1704 void
1705 MidiRegionView::get_patch_key_at (Evoral::MusicalTime time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key)
1706 {
1707         MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1708         while (i != _model->patch_changes().end() && (*i)->channel() != channel) {
1709                 ++i;
1710         }
1711
1712         if (i != _model->patch_changes().end()) {
1713                 key.msb = (*i)->bank_msb ();
1714                 key.lsb = (*i)->bank_lsb ();
1715                 key.program_number = (*i)->program ();
1716         } else {
1717                 key.msb = key.lsb = key.program_number = 0;
1718         }
1719
1720         assert (key.is_sane());
1721 }
1722
1723
1724 void
1725 MidiRegionView::change_patch_change (CanvasPatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1726 {
1727         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1728
1729         if (pc.patch()->program() != new_patch.program_number) {
1730                 c->change_program (pc.patch (), new_patch.program_number);
1731         }
1732
1733         int const new_bank = (new_patch.msb << 7) | new_patch.lsb;
1734         if (pc.patch()->bank() != new_bank) {
1735                 c->change_bank (pc.patch (), new_bank);
1736         }
1737
1738         _model->apply_command (*trackview.session(), c);
1739
1740         _patch_changes.clear ();
1741         display_patch_changes ();
1742 }
1743
1744 void
1745 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::MusicalTime> & new_change)
1746 {
1747         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("alter patch change"));
1748
1749         if (old_change->time() != new_change.time()) {
1750                 c->change_time (old_change, new_change.time());
1751         }
1752
1753         if (old_change->channel() != new_change.channel()) {
1754                 c->change_channel (old_change, new_change.channel());
1755         }
1756
1757         if (old_change->program() != new_change.program()) {
1758                 c->change_program (old_change, new_change.program());
1759         }
1760
1761         if (old_change->bank() != new_change.bank()) {
1762                 c->change_bank (old_change, new_change.bank());
1763         }
1764
1765         _model->apply_command (*trackview.session(), c);
1766
1767         _patch_changes.clear ();
1768         display_patch_changes ();
1769 }
1770
1771 /** Add a patch change to the region.
1772  *  @param t Time in frames relative to region position
1773  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
1774  *  MidiTimeAxisView::get_channel_for_add())
1775  */
1776 void
1777 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::MusicalTime> const & patch)
1778 {
1779         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1780
1781         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("add patch change"));
1782         c->add (MidiModel::PatchChangePtr (
1783                         new Evoral::PatchChange<Evoral::MusicalTime> (
1784                                 absolute_frames_to_source_beats (_region->position() + t),
1785                                 mtv->get_channel_for_add(), patch.program(), patch.bank()
1786                                 )
1787                         )
1788                 );
1789
1790         _model->apply_command (*trackview.session(), c);
1791
1792         _patch_changes.clear ();
1793         display_patch_changes ();
1794 }
1795
1796 void
1797 MidiRegionView::move_patch_change (CanvasPatchChange& pc, Evoral::MusicalTime t)
1798 {
1799         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
1800         c->change_time (pc.patch (), t);
1801         _model->apply_command (*trackview.session(), c);
1802
1803         _patch_changes.clear ();
1804         display_patch_changes ();
1805 }
1806
1807 void
1808 MidiRegionView::delete_patch_change (CanvasPatchChange* pc)
1809 {
1810         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
1811         c->remove (pc->patch ());
1812         _model->apply_command (*trackview.session(), c);
1813
1814         _patch_changes.clear ();
1815         display_patch_changes ();
1816 }
1817
1818 void
1819 MidiRegionView::previous_patch (CanvasPatchChange& patch)
1820 {
1821         if (patch.patch()->program() < 127) {
1822                 MIDI::Name::PatchPrimaryKey key;
1823                 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1824                 key.program_number++;
1825                 change_patch_change (patch, key);
1826         }
1827 }
1828
1829 void
1830 MidiRegionView::next_patch (CanvasPatchChange& patch)
1831 {
1832         if (patch.patch()->program() > 0) {
1833                 MIDI::Name::PatchPrimaryKey key;
1834                 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1835                 key.program_number--;
1836                 change_patch_change (patch, key);
1837         }
1838 }
1839
1840 void
1841 MidiRegionView::previous_bank (CanvasPatchChange& patch)
1842 {
1843         if (patch.patch()->program() < 127) {
1844                 MIDI::Name::PatchPrimaryKey key;
1845                 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1846                 if (key.lsb > 0) {
1847                         key.lsb--;
1848                         change_patch_change (patch, key);
1849                 } else {
1850                         if (key.msb > 0) {
1851                                 key.lsb = 127;
1852                                 key.msb--;
1853                                 change_patch_change (patch, key);
1854                         }
1855                 }
1856         }
1857 }
1858
1859 void
1860 MidiRegionView::next_bank (CanvasPatchChange& patch)
1861 {
1862         if (patch.patch()->program() > 0) {
1863                 MIDI::Name::PatchPrimaryKey key;
1864                 get_patch_key_at (patch.patch()->time(), patch.patch()->channel(), key);
1865                 if (key.lsb < 127) {
1866                         key.lsb++;
1867                         change_patch_change (patch, key);
1868                 } else {
1869                         if (key.msb < 127) {
1870                                 key.lsb = 0;
1871                                 key.msb++;
1872                                 change_patch_change (patch, key);
1873                         }
1874                 }
1875         }
1876 }
1877
1878 void
1879 MidiRegionView::maybe_remove_deleted_note_from_selection (CanvasNoteEvent* cne)
1880 {
1881         if (_selection.empty()) {
1882                 return;
1883         }
1884
1885         _selection.erase (cne);
1886 }
1887
1888 void
1889 MidiRegionView::delete_selection()
1890 {
1891         if (_selection.empty()) {
1892                 return;
1893         }
1894
1895         start_note_diff_command (_("delete selection"));
1896
1897         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
1898                 if ((*i)->selected()) {
1899                         _note_diff_command->remove((*i)->note());
1900                 }
1901         }
1902
1903         _selection.clear();
1904
1905         apply_diff ();
1906 }
1907
1908 void
1909 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
1910 {
1911         start_note_diff_command (_("delete note"));
1912         _note_diff_command->remove (n);
1913         apply_diff ();
1914
1915         trackview.editor().verbose_cursor()->hide ();
1916 }
1917
1918 void
1919 MidiRegionView::clear_selection_except (ArdourCanvas::CanvasNoteEvent* ev, bool signal)
1920 {
1921         bool changed = false;
1922
1923         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
1924                 if ((*i) != ev) {
1925                         Selection::iterator tmp = i;
1926                         ++tmp;
1927
1928                         (*i)->set_selected (false);
1929                         (*i)->hide_velocity ();
1930                         _selection.erase (i);
1931                         changed = true;
1932
1933                         i = tmp;
1934                 } else {
1935                         ++i;
1936                 }
1937         }
1938
1939         /* this does not change the status of this regionview w.r.t the editor
1940            selection.
1941         */
1942
1943         if (changed && signal) {
1944                 SelectionCleared (this); /* EMIT SIGNAL */
1945         }
1946 }
1947
1948 void
1949 MidiRegionView::unique_select(ArdourCanvas::CanvasNoteEvent* ev)
1950 {
1951         clear_selection_except (ev);
1952
1953         /* don't bother with checking to see if we should remove this
1954            regionview from the editor selection, since we're about to add
1955            another note, and thus put/keep this regionview in the editor
1956            selection anyway.
1957         */
1958
1959         if (!ev->selected()) {
1960                 add_to_selection (ev);
1961         }
1962 }
1963
1964 void
1965 MidiRegionView::select_all_notes ()
1966 {
1967         clear_selection ();
1968
1969         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1970                 add_to_selection (*i);
1971         }
1972 }
1973
1974 void
1975 MidiRegionView::select_range (framepos_t start, framepos_t end)
1976 {
1977         clear_selection ();
1978
1979         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1980                 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
1981                 if (t >= start && t <= end) {
1982                         add_to_selection (*i);
1983                 }
1984         }
1985 }
1986
1987 void
1988 MidiRegionView::invert_selection ()
1989 {
1990         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1991                 if ((*i)->selected()) {
1992                         remove_from_selection(*i);
1993                 } else {
1994                         add_to_selection (*i);
1995                 }
1996         }
1997 }
1998
1999 void
2000 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2001 {
2002         uint8_t low_note = 127;
2003         uint8_t high_note = 0;
2004         MidiModel::Notes& notes (_model->notes());
2005         _optimization_iterator = _events.begin();
2006
2007         if (!add) {
2008                 clear_selection ();
2009         }
2010
2011         if (extend && _selection.empty()) {
2012                 extend = false;
2013         }
2014
2015         if (extend) {
2016
2017                 /* scan existing selection to get note range */
2018
2019                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2020                         if ((*i)->note()->note() < low_note) {
2021                                 low_note = (*i)->note()->note();
2022                         }
2023                         if ((*i)->note()->note() > high_note) {
2024                                 high_note = (*i)->note()->note();
2025                         }
2026                 }
2027
2028                 low_note = min (low_note, notenum);
2029                 high_note = max (high_note, notenum);
2030         }
2031
2032         _no_sound_notes = true;
2033
2034         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2035
2036                 boost::shared_ptr<NoteType> note (*n);
2037                 CanvasNoteEvent* cne;
2038                 bool select = false;
2039
2040                 if (((1 << note->channel()) & channel_mask) != 0) {
2041                         if (extend) {
2042                                 if ((note->note() >= low_note && note->note() <= high_note)) {
2043                                         select = true;
2044                                 }
2045                         } else if (note->note() == notenum) {
2046                                 select = true;
2047                         }
2048                 }
2049
2050                 if (select) {
2051                         if ((cne = find_canvas_note (note)) != 0) {
2052                                 // extend is false because we've taken care of it,
2053                                 // since it extends by time range, not pitch.
2054                                 note_selected (cne, add, false);
2055                         }
2056                 }
2057
2058                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2059
2060         }
2061
2062         _no_sound_notes = false;
2063 }
2064
2065 void
2066 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2067 {
2068         MidiModel::Notes& notes (_model->notes());
2069         _optimization_iterator = _events.begin();
2070
2071         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2072
2073                 boost::shared_ptr<NoteType> note (*n);
2074                 CanvasNoteEvent* cne;
2075
2076                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2077                         if ((cne = find_canvas_note (note)) != 0) {
2078                                 if (cne->selected()) {
2079                                         note_deselected (cne);
2080                                 } else {
2081                                         note_selected (cne, true, false);
2082                                 }
2083                         }
2084                 }
2085         }
2086 }
2087
2088 void
2089 MidiRegionView::note_selected (ArdourCanvas::CanvasNoteEvent* ev, bool add, bool extend)
2090 {
2091         if (!add) {
2092                 clear_selection_except (ev);
2093                 if (!_selection.empty()) {
2094                         PublicEditor& editor (trackview.editor());
2095                         editor.get_selection().add (this);
2096                 }
2097         }
2098
2099         if (!extend) {
2100
2101                 if (!ev->selected()) {
2102                         add_to_selection (ev);
2103                 }
2104
2105         } else {
2106                 /* find end of latest note selected, select all between that and the start of "ev" */
2107
2108                 Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2109                 Evoral::MusicalTime latest = 0;
2110
2111                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2112                         if ((*i)->note()->end_time() > latest) {
2113                                 latest = (*i)->note()->end_time();
2114                         }
2115                         if ((*i)->note()->time() < earliest) {
2116                                 earliest = (*i)->note()->time();
2117                         }
2118                 }
2119
2120                 if (ev->note()->end_time() > latest) {
2121                         latest = ev->note()->end_time();
2122                 }
2123
2124                 if (ev->note()->time() < earliest) {
2125                         earliest = ev->note()->time();
2126                 }
2127
2128                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2129
2130                         /* find notes entirely within OR spanning the earliest..latest range */
2131
2132                         if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2133                             ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2134                                 add_to_selection (*i);
2135                         }
2136
2137                 }
2138         }
2139 }
2140
2141 void
2142 MidiRegionView::note_deselected(ArdourCanvas::CanvasNoteEvent* ev)
2143 {
2144         remove_from_selection (ev);
2145 }
2146
2147 void
2148 MidiRegionView::update_drag_selection(double x1, double x2, double y1, double y2, bool extend)
2149 {
2150         if (x1 > x2) {
2151                 swap (x1, x2);
2152         }
2153
2154         if (y1 > y2) {
2155                 swap (y1, y2);
2156         }
2157
2158         // TODO: Make this faster by storing the last updated selection rect, and only
2159         // adjusting things that are in the area that appears/disappeared.
2160         // We probably need a tree to be able to find events in O(log(n)) time.
2161
2162         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2163
2164                 /* check if any corner of the note is inside the rect
2165
2166                    Notes:
2167                    1) this is computing "touched by", not "contained by" the rect.
2168                    2) this does not require that events be sorted in time.
2169                 */
2170
2171                 const double ix1 = (*i)->x1();
2172                 const double ix2 = (*i)->x2();
2173                 const double iy1 = (*i)->y1();
2174                 const double iy2 = (*i)->y2();
2175
2176                 if ((ix1 >= x1 && ix1 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2177                     (ix1 >= x1 && ix1 <= x2 && iy2 >= y1 && iy2 <= y2) ||
2178                     (ix2 >= x1 && ix2 <= x2 && iy1 >= y1 && iy1 <= y2) ||
2179                     (ix2 >= x1 && ix2 <= x2 && iy2 >= y1 && iy2 <= y2)) {
2180
2181                         // Inside rectangle
2182                         if (!(*i)->selected()) {
2183                                 add_to_selection (*i);
2184                         }
2185                 } else if ((*i)->selected() && !extend) {
2186                         // Not inside rectangle
2187                         remove_from_selection (*i);
2188                 }
2189         }
2190 }
2191
2192 void
2193 MidiRegionView::remove_from_selection (CanvasNoteEvent* ev)
2194 {
2195         Selection::iterator i = _selection.find (ev);
2196
2197         if (i != _selection.end()) {
2198                 _selection.erase (i);
2199         }
2200
2201         ev->set_selected (false);
2202         ev->hide_velocity ();
2203
2204         if (_selection.empty()) {
2205                 PublicEditor& editor (trackview.editor());
2206                 editor.get_selection().remove (this);
2207         }
2208 }
2209
2210 void
2211 MidiRegionView::add_to_selection (CanvasNoteEvent* ev)
2212 {
2213         bool add_mrv_selection = false;
2214
2215         if (_selection.empty()) {
2216                 add_mrv_selection = true;
2217         }
2218
2219         if (_selection.insert (ev).second) {
2220                 ev->set_selected (true);
2221                 play_midi_note ((ev)->note());
2222         }
2223
2224         if (add_mrv_selection) {
2225                 PublicEditor& editor (trackview.editor());
2226                 editor.get_selection().add (this);
2227         }
2228 }
2229
2230 void
2231 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2232 {
2233         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2234         PossibleChord to_play;
2235         Evoral::MusicalTime earliest = Evoral::MaxMusicalTime;
2236
2237         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2238                 if ((*i)->note()->time() < earliest) {
2239                         earliest = (*i)->note()->time();
2240                 }
2241         }
2242
2243         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2244                 if (Evoral::musical_time_equal ((*i)->note()->time(), earliest)) {
2245                         to_play.push_back ((*i)->note());
2246                 }
2247                 (*i)->move_event(dx, dy);
2248         }
2249
2250         if (dy && !_selection.empty() && !_no_sound_notes && Config->get_sound_midi_notes()) {
2251
2252                 if (to_play.size() > 1) {
2253
2254                         PossibleChord shifted;
2255
2256                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2257                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2258                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2259                                 shifted.push_back (moved_note);
2260                         }
2261
2262                         play_midi_chord (shifted);
2263
2264                 } else if (!to_play.empty()) {
2265
2266                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2267                         moved_note->set_note (moved_note->note() + cumulative_dy);
2268                         play_midi_note (moved_note);
2269                 }
2270         }
2271 }
2272
2273 void
2274 MidiRegionView::note_dropped(CanvasNoteEvent *, frameoffset_t dt, int8_t dnote)
2275 {
2276         assert (!_selection.empty());
2277
2278         uint8_t lowest_note_in_selection  = 127;
2279         uint8_t highest_note_in_selection = 0;
2280         uint8_t highest_note_difference = 0;
2281
2282         // find highest and lowest notes first
2283
2284         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2285                 uint8_t pitch = (*i)->note()->note();
2286                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2287                 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2288         }
2289
2290         /*
2291           cerr << "dnote: " << (int) dnote << endl;
2292           cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2293           << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2294           cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2295           << int(highest_note_in_selection) << endl;
2296           cerr << "selection size: " << _selection.size() << endl;
2297           cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2298         */
2299
2300         // Make sure the note pitch does not exceed the MIDI standard range
2301         if (highest_note_in_selection + dnote > 127) {
2302                 highest_note_difference = highest_note_in_selection - 127;
2303         }
2304
2305         start_note_diff_command (_("move notes"));
2306
2307         for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2308
2309                 Evoral::MusicalTime new_time = absolute_frames_to_source_beats (source_beats_to_absolute_frames ((*i)->note()->time()) + dt);
2310
2311                 if (new_time < 0) {
2312                         continue;
2313                 }
2314
2315                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2316
2317                 uint8_t original_pitch = (*i)->note()->note();
2318                 uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2319
2320                 // keep notes in standard midi range
2321                 clamp_to_0_127(new_pitch);
2322
2323                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2324                 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2325
2326                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2327         }
2328
2329         apply_diff();
2330
2331         // care about notes being moved beyond the upper/lower bounds on the canvas
2332         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
2333             highest_note_in_selection > midi_stream_view()->highest_note()) {
2334                 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2335         }
2336 }
2337
2338 /** @param x Pixel relative to the region position.
2339  *  @return Snapped frame relative to the region position.
2340  */
2341 framepos_t
2342 MidiRegionView::snap_pixel_to_frame(double x)
2343 {
2344         PublicEditor& editor (trackview.editor());
2345         return snap_frame_to_frame (editor.pixel_to_frame (x));
2346 }
2347
2348 /** @param x Pixel relative to the region position.
2349  *  @return Snapped pixel relative to the region position.
2350  */
2351 double
2352 MidiRegionView::snap_to_pixel(double x)
2353 {
2354         return (double) trackview.editor().frame_to_pixel(snap_pixel_to_frame(x));
2355 }
2356
2357 double
2358 MidiRegionView::get_position_pixels()
2359 {
2360         framepos_t region_frame = get_position();
2361         return trackview.editor().frame_to_pixel(region_frame);
2362 }
2363
2364 double
2365 MidiRegionView::get_end_position_pixels()
2366 {
2367         framepos_t frame = get_position() + get_duration ();
2368         return trackview.editor().frame_to_pixel(frame);
2369 }
2370
2371 framepos_t
2372 MidiRegionView::source_beats_to_absolute_frames(double beats) const
2373 {
2374         /* the time converter will return the frame corresponding to `beats'
2375            relative to the start of the source. The start of the source
2376            is an implied position given by region->position - region->start
2377         */
2378         const framepos_t source_start = _region->position() - _region->start();
2379         return  source_start +  _source_relative_time_converter.to (beats);
2380 }
2381
2382 double
2383 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2384 {
2385         /* the `frames' argument needs to be converted into a frame count
2386            relative to the start of the source before being passed in to the
2387            converter.
2388         */
2389         const framepos_t source_start = _region->position() - _region->start();
2390         return  _source_relative_time_converter.from (frames - source_start);
2391 }
2392
2393 framepos_t
2394 MidiRegionView::region_beats_to_region_frames(double beats) const
2395 {
2396         return _region_relative_time_converter.to(beats);
2397 }
2398
2399 double
2400 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2401 {
2402         return _region_relative_time_converter.from(frames);
2403 }
2404
2405 void
2406 MidiRegionView::begin_resizing (bool /*at_front*/)
2407 {
2408         _resize_data.clear();
2409
2410         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2411                 CanvasNote *note = dynamic_cast<CanvasNote *> (*i);
2412
2413                 // only insert CanvasNotes into the map
2414                 if (note) {
2415                         NoteResizeData *resize_data = new NoteResizeData();
2416                         resize_data->canvas_note = note;
2417
2418                         // create a new SimpleRect from the note which will be the resize preview
2419                         SimpleRect *resize_rect = new SimpleRect(
2420                                 *_note_group, note->x1(), note->y1(), note->x2(), note->y2());
2421
2422                         // calculate the colors: get the color settings
2423                         uint32_t fill_color = UINT_RGBA_CHANGE_A(
2424                                 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get(),
2425                                 128);
2426
2427                         // make the resize preview notes more transparent and bright
2428                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2429
2430                         // calculate color based on note velocity
2431                         resize_rect->property_fill_color_rgba() = UINT_INTERPOLATE(
2432                                 CanvasNoteEvent::meter_style_fill_color(note->note()->velocity(), note->selected()),
2433                                 fill_color,
2434                                 0.85);
2435
2436                         resize_rect->property_outline_color_rgba() = CanvasNoteEvent::calculate_outline(
2437                                 ARDOUR_UI::config()->canvasvar_MidiNoteSelected.get());
2438
2439                         resize_data->resize_rect = resize_rect;
2440                         _resize_data.push_back(resize_data);
2441                 }
2442         }
2443 }
2444
2445 /** Update resizing notes while user drags.
2446  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2447  * @param at_front which end of the note (true == note on, false == note off)
2448  * @param delta_x change in mouse position since the start of the drag
2449  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2450  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2451  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2452  * as the \a primary note.
2453  */
2454 void
2455 MidiRegionView::update_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2456 {
2457         bool cursor_set = false;
2458
2459         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2460                 SimpleRect* resize_rect = (*i)->resize_rect;
2461                 CanvasNote* canvas_note = (*i)->canvas_note;
2462                 double current_x;
2463
2464                 if (at_front) {
2465                         if (relative) {
2466                                 current_x = canvas_note->x1() + delta_x;
2467                         } else {
2468                                 current_x = primary->x1() + delta_x;
2469                         }
2470                 } else {
2471                         if (relative) {
2472                                 current_x = canvas_note->x2() + delta_x;
2473                         } else {
2474                                 current_x = primary->x2() + delta_x;
2475                         }
2476                 }
2477
2478                 if (at_front) {
2479                         resize_rect->property_x1() = snap_to_pixel(current_x);
2480                         resize_rect->property_x2() = canvas_note->x2();
2481                 } else {
2482                         resize_rect->property_x2() = snap_to_pixel(current_x);
2483                         resize_rect->property_x1() = canvas_note->x1();
2484                 }
2485
2486                 if (!cursor_set) {
2487                         double beats;
2488
2489                         beats = snap_pixel_to_frame (current_x);
2490                         beats = region_frames_to_region_beats (beats);
2491
2492                         double len;
2493
2494                         if (at_front) {
2495                                 if (beats < canvas_note->note()->end_time()) {
2496                                         len = canvas_note->note()->time() - beats;
2497                                         len += canvas_note->note()->length();
2498                                 } else {
2499                                         len = 0;
2500                                 }
2501                         } else {
2502                                 if (beats >= canvas_note->note()->time()) {
2503                                         len = beats - canvas_note->note()->time();
2504                                 } else {
2505                                         len = 0;
2506                                 }
2507                         }
2508
2509                         char buf[16];
2510                         snprintf (buf, sizeof (buf), "%.3g beats", len);
2511                         show_verbose_cursor (buf, 0, 0);
2512
2513                         cursor_set = true;
2514                 }
2515
2516         }
2517 }
2518
2519
2520 /** Finish resizing notes when the user releases the mouse button.
2521  *  Parameters the same as for \a update_resizing().
2522  */
2523 void
2524 MidiRegionView::commit_resizing (ArdourCanvas::CanvasNoteEvent* primary, bool at_front, double delta_x, bool relative)
2525 {
2526         start_note_diff_command (_("resize notes"));
2527
2528         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2529                 CanvasNote*  canvas_note = (*i)->canvas_note;
2530                 SimpleRect*  resize_rect = (*i)->resize_rect;
2531
2532                 /* Get the new x position for this resize, which is in pixels relative
2533                  * to the region position.
2534                  */
2535                 
2536                 double current_x;
2537
2538                 if (at_front) {
2539                         if (relative) {
2540                                 current_x = canvas_note->x1() + delta_x;
2541                         } else {
2542                                 current_x = primary->x1() + delta_x;
2543                         }
2544                 } else {
2545                         if (relative) {
2546                                 current_x = canvas_note->x2() + delta_x;
2547                         } else {
2548                                 current_x = primary->x2() + delta_x;
2549                         }
2550                 }
2551
2552                 /* Convert that to a frame within the source */
2553                 current_x = snap_pixel_to_frame (current_x) + _region->start ();
2554
2555                 /* and then to beats */
2556                 current_x = region_frames_to_region_beats (current_x);
2557
2558                 if (at_front && current_x < canvas_note->note()->end_time()) {
2559                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, current_x);
2560
2561                         double len = canvas_note->note()->time() - current_x;
2562                         len += canvas_note->note()->length();
2563
2564                         if (len > 0) {
2565                                 /* XXX convert to beats */
2566                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2567                         }
2568                 }
2569
2570                 if (!at_front) {
2571                         double len = current_x - canvas_note->note()->time();
2572
2573                         if (len > 0) {
2574                                 /* XXX convert to beats */
2575                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2576                         }
2577                 }
2578
2579                 delete resize_rect;
2580                 delete (*i);
2581         }
2582
2583         _resize_data.clear();
2584         apply_diff();
2585 }
2586
2587 void
2588 MidiRegionView::abort_resizing ()
2589 {
2590         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2591                 delete (*i)->resize_rect;
2592                 delete *i;
2593         }
2594
2595         _resize_data.clear ();
2596 }
2597
2598 void
2599 MidiRegionView::change_note_velocity(CanvasNoteEvent* event, int8_t velocity, bool relative)
2600 {
2601         uint8_t new_velocity;
2602
2603         if (relative) {
2604                 new_velocity = event->note()->velocity() + velocity;
2605                 clamp_to_0_127(new_velocity);
2606         } else {
2607                 new_velocity = velocity;
2608         }
2609
2610         event->set_selected (event->selected()); // change color
2611
2612         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2613 }
2614
2615 void
2616 MidiRegionView::change_note_note (CanvasNoteEvent* event, int8_t note, bool relative)
2617 {
2618         uint8_t new_note;
2619
2620         if (relative) {
2621                 new_note = event->note()->note() + note;
2622         } else {
2623                 new_note = note;
2624         }
2625
2626         clamp_to_0_127 (new_note);
2627         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2628 }
2629
2630 void
2631 MidiRegionView::trim_note (CanvasNoteEvent* event, Evoral::MusicalTime front_delta, Evoral::MusicalTime end_delta)
2632 {
2633         bool change_start = false;
2634         bool change_length = false;
2635         Evoral::MusicalTime new_start = 0;
2636         Evoral::MusicalTime new_length = 0;
2637
2638         /* NOTE: the semantics of the two delta arguments are slightly subtle:
2639
2640            front_delta: if positive - move the start of the note later in time (shortening it)
2641            if negative - move the start of the note earlier in time (lengthening it)
2642
2643            end_delta:   if positive - move the end of the note later in time (lengthening it)
2644            if negative - move the end of the note earlier in time (shortening it)
2645         */
2646
2647         if (front_delta) {
2648                 if (front_delta < 0) {
2649
2650                         if (event->note()->time() < -front_delta) {
2651                                 new_start = 0;
2652                         } else {
2653                                 new_start = event->note()->time() + front_delta; // moves earlier
2654                         }
2655
2656                         /* start moved toward zero, so move the end point out to where it used to be.
2657                            Note that front_delta is negative, so this increases the length.
2658                         */
2659
2660                         new_length = event->note()->length() - front_delta;
2661                         change_start = true;
2662                         change_length = true;
2663
2664                 } else {
2665
2666                         Evoral::MusicalTime new_pos = event->note()->time() + front_delta;
2667
2668                         if (new_pos < event->note()->end_time()) {
2669                                 new_start = event->note()->time() + front_delta;
2670                                 /* start moved toward the end, so move the end point back to where it used to be */
2671                                 new_length = event->note()->length() - front_delta;
2672                                 change_start = true;
2673                                 change_length = true;
2674                         }
2675                 }
2676
2677         }
2678
2679         if (end_delta) {
2680                 bool can_change = true;
2681                 if (end_delta < 0) {
2682                         if (event->note()->length() < -end_delta) {
2683                                 can_change = false;
2684                         }
2685                 }
2686
2687                 if (can_change) {
2688                         new_length = event->note()->length() + end_delta;
2689                         change_length = true;
2690                 }
2691         }
2692
2693         if (change_start) {
2694                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2695         }
2696
2697         if (change_length) {
2698                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2699         }
2700 }
2701
2702 void
2703 MidiRegionView::change_note_channel (CanvasNoteEvent* event, int8_t chn, bool relative)
2704 {
2705         uint8_t new_channel;
2706
2707         if (relative) {
2708                 if (chn < 0.0) {
2709                         if (event->note()->channel() < -chn) {
2710                                 new_channel = 0;
2711                         } else {
2712                                 new_channel = event->note()->channel() + chn;
2713                         }
2714                 } else {
2715                         new_channel = event->note()->channel() + chn;
2716                 }
2717         } else {
2718                 new_channel = (uint8_t) chn;
2719         }
2720
2721         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
2722 }
2723
2724 void
2725 MidiRegionView::change_note_time (CanvasNoteEvent* event, Evoral::MusicalTime delta, bool relative)
2726 {
2727         Evoral::MusicalTime new_time;
2728
2729         if (relative) {
2730                 if (delta < 0.0) {
2731                         if (event->note()->time() < -delta) {
2732                                 new_time = 0;
2733                         } else {
2734                                 new_time = event->note()->time() + delta;
2735                         }
2736                 } else {
2737                         new_time = event->note()->time() + delta;
2738                 }
2739         } else {
2740                 new_time = delta;
2741         }
2742
2743         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
2744 }
2745
2746 void
2747 MidiRegionView::change_note_length (CanvasNoteEvent* event, Evoral::MusicalTime t)
2748 {
2749         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
2750 }
2751
2752 void
2753 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush)
2754 {
2755         int8_t delta;
2756
2757         if (_selection.empty()) {
2758                 return;
2759         }
2760
2761         if (fine) {
2762                 delta = 1;
2763         } else {
2764                 delta = 10;
2765         }
2766
2767         if (!up) {
2768                 delta = -delta;
2769         }
2770
2771         if (!allow_smush) {
2772                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2773                         if ((*i)->note()->velocity() + delta == 0 || (*i)->note()->velocity() + delta == 127) {
2774                                 return;
2775                         }
2776                 }
2777         }
2778
2779         start_note_diff_command (_("change velocities"));
2780
2781         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
2782                 Selection::iterator next = i;
2783                 ++next;
2784                 change_note_velocity (*i, delta, true);
2785                 i = next;
2786         }
2787
2788         apply_diff();
2789
2790         if (!_selection.empty()) {
2791                 char buf[24];
2792                 snprintf (buf, sizeof (buf), "Vel %d",
2793                           (int) (*_selection.begin())->note()->velocity());
2794                 show_verbose_cursor (buf, 10, 10);
2795         }
2796 }
2797
2798
2799 void
2800 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
2801 {
2802         if (_selection.empty()) {
2803                 return;
2804         }
2805
2806         int8_t delta;
2807
2808         if (fine) {
2809                 delta = 1;
2810         } else {
2811                 delta = 12;
2812         }
2813
2814         if (!up) {
2815                 delta = -delta;
2816         }
2817
2818         if (!allow_smush) {
2819                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2820                         if (!up) {
2821                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
2822                                         return;
2823                                 }
2824                         } else {
2825                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
2826                                         return;
2827                                 }
2828                         }
2829                 }
2830         }
2831
2832         start_note_diff_command (_("transpose"));
2833
2834         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2835                 Selection::iterator next = i;
2836                 ++next;
2837                 change_note_note (*i, delta, true);
2838                 i = next;
2839         }
2840
2841         apply_diff ();
2842 }
2843
2844 void
2845 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::MusicalTime delta, bool start, bool end)
2846 {
2847         if (delta == 0.0) {
2848                 if (fine) {
2849                         delta = 1.0/128.0;
2850                 } else {
2851                         /* grab the current grid distance */
2852                         bool success;
2853                         delta = trackview.editor().get_grid_type_as_beats (success, _region->position());
2854                         if (!success) {
2855                                 /* XXX cannot get grid type as beats ... should always be possible ... FIX ME */
2856                                 cerr << "Grid type not available as beats - TO BE FIXED\n";
2857                                 return;
2858                         }
2859                 }
2860         }
2861
2862         if (shorter) {
2863                 delta = -delta;
2864         }
2865
2866         start_note_diff_command (_("change note lengths"));
2867
2868         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2869                 Selection::iterator next = i;
2870                 ++next;
2871
2872                 /* note the negation of the delta for start */
2873
2874                 trim_note (*i, (start ? -delta : 0), (end ? delta : 0));
2875                 i = next;
2876         }
2877
2878         apply_diff ();
2879
2880 }
2881
2882 void
2883 MidiRegionView::nudge_notes (bool forward)
2884 {
2885         if (_selection.empty()) {
2886                 return;
2887         }
2888
2889         /* pick a note as the point along the timeline to get the nudge distance.
2890            its not necessarily the earliest note, so we may want to pull the notes out
2891            into a vector and sort before using the first one.
2892         */
2893
2894         framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
2895         framepos_t unused;
2896         framepos_t distance;
2897
2898         if (trackview.editor().snap_mode() == Editing::SnapOff) {
2899
2900                 /* grid is off - use nudge distance */
2901
2902                 distance = trackview.editor().get_nudge_distance (ref_point, unused);
2903
2904         } else {
2905
2906                 /* use grid */
2907
2908                 framepos_t next_pos = ref_point;
2909
2910                 if (forward) {
2911                         if (max_framepos - 1 < next_pos) {
2912                                 next_pos += 1;
2913                         }
2914                 } else {
2915                         if (next_pos == 0) {
2916                                 return;
2917                         }
2918                         next_pos -= 1;
2919                 }
2920
2921                 trackview.editor().snap_to (next_pos, (forward ? 1 : -1), false);
2922                 distance = ref_point - next_pos;
2923         }
2924
2925         if (distance == 0) {
2926                 return;
2927         }
2928
2929         Evoral::MusicalTime delta = region_frames_to_region_beats (fabs (distance));
2930
2931         if (!forward) {
2932                 delta = -delta;
2933         }
2934
2935         start_note_diff_command (_("nudge"));
2936
2937         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
2938                 Selection::iterator next = i;
2939                 ++next;
2940                 change_note_time (*i, delta, true);
2941                 i = next;
2942         }
2943
2944         apply_diff ();
2945 }
2946
2947 void
2948 MidiRegionView::change_channel(uint8_t channel)
2949 {
2950         start_note_diff_command(_("change channel"));
2951         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2952                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
2953         }
2954
2955         apply_diff();
2956 }
2957
2958
2959 void
2960 MidiRegionView::note_entered(ArdourCanvas::CanvasNoteEvent* ev)
2961 {
2962         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2963
2964         _pre_enter_cursor = editor->get_canvas_cursor ();
2965
2966         if (_mouse_state == SelectTouchDragging) {
2967                 note_selected (ev, true);
2968         }
2969
2970         show_verbose_cursor (ev->note ());
2971 }
2972
2973 void
2974 MidiRegionView::note_left (ArdourCanvas::CanvasNoteEvent*)
2975 {
2976         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
2977
2978         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2979                 (*i)->hide_velocity ();
2980         }
2981
2982         editor->verbose_cursor()->hide ();
2983
2984         if (_pre_enter_cursor) {
2985                 editor->set_canvas_cursor (_pre_enter_cursor);
2986                 _pre_enter_cursor = 0;
2987         }
2988 }
2989
2990 void
2991 MidiRegionView::patch_entered (ArdourCanvas::CanvasPatchChange* ev)
2992 {
2993         ostringstream s;
2994         /* XXX should get patch name if we can */
2995         s << _("Bank:") << (ev->patch()->bank() + MIDI_BP_ZERO) << '\n' << _("Program:") << ((int) ev->patch()->program()) + MIDI_BP_ZERO << '\n' << _("Channel:") << ((int) ev->patch()->channel() + 1);
2996         show_verbose_cursor (s.str(), 10, 20);
2997 }
2998
2999 void
3000 MidiRegionView::patch_left (ArdourCanvas::CanvasPatchChange *)
3001 {
3002         trackview.editor().verbose_cursor()->hide ();
3003 }
3004
3005 void
3006 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3007 {
3008         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3009
3010         if (x_fraction > 0.0 && x_fraction < 0.25) {
3011                 editor->set_canvas_cursor (editor->cursors()->left_side_trim);
3012         } else if (x_fraction >= 0.75 && x_fraction < 1.0) {
3013                 editor->set_canvas_cursor (editor->cursors()->right_side_trim);
3014         } else {
3015                 if (_pre_enter_cursor && can_set_cursor) {
3016                         editor->set_canvas_cursor (_pre_enter_cursor);
3017                 }
3018         }
3019 }
3020
3021 void
3022 MidiRegionView::set_frame_color()
3023 {
3024         uint32_t f;
3025
3026         TimeAxisViewItem::set_frame_color ();
3027
3028         if (!frame) {
3029                 return;
3030         }
3031
3032         if (_selected) {
3033                 f = ARDOUR_UI::config()->canvasvar_SelectedFrameBase.get();
3034         } else if (high_enough_for_name) {
3035                 f= ARDOUR_UI::config()->canvasvar_MidiFrameBase.get();
3036         } else {
3037                 f = fill_color;
3038         }
3039
3040         if (!rect_visible) {
3041                 f = UINT_RGBA_CHANGE_A (f, 0);
3042         }
3043
3044         frame->property_fill_color_rgba() = f;
3045 }
3046
3047 void
3048 MidiRegionView::midi_channel_mode_changed(ChannelMode mode, uint16_t mask)
3049 {
3050         switch (mode) {
3051         case AllChannels:
3052         case FilterChannels:
3053                 _force_channel = -1;
3054                 break;
3055         case ForceChannel:
3056                 _force_channel = mask;
3057                 mask = 0xFFFF; // Show all notes as active (below)
3058         };
3059
3060         // Update notes for selection
3061         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3062                 (*i)->on_channel_selection_change(mask);
3063         }
3064
3065         _last_channel_selection = mask;
3066
3067         _patch_changes.clear ();
3068         display_patch_changes ();
3069 }
3070
3071 void
3072 MidiRegionView::midi_patch_settings_changed(std::string model, std::string custom_device_mode)
3073 {
3074         _model_name         = model;
3075         _custom_device_mode = custom_device_mode;
3076         redisplay_model();
3077 }
3078
3079 void
3080 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3081 {
3082         if (_selection.empty()) {
3083                 return;
3084         }
3085
3086         PublicEditor& editor (trackview.editor());
3087
3088         switch (op) {
3089         case Delete:
3090                 /* XXX what to do ? */
3091                 break;
3092         case Cut:
3093         case Copy:
3094                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3095                 break;
3096         default:
3097                 break;
3098         }
3099
3100         if (op != Copy) {
3101
3102                 start_note_diff_command();
3103
3104                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3105                         switch (op) {
3106                         case Copy:
3107                                 break;
3108                         case Delete:
3109                         case Cut:
3110                         case Clear:
3111                                 note_diff_remove_note (*i);
3112                                 break;
3113                         }
3114                 }
3115
3116                 apply_diff();
3117         }
3118 }
3119
3120 MidiCutBuffer*
3121 MidiRegionView::selection_as_cut_buffer () const
3122 {
3123         Notes notes;
3124
3125         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3126                 NoteType* n = (*i)->note().get();
3127                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3128         }
3129
3130         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3131         cb->set (notes);
3132
3133         return cb;
3134 }
3135
3136 /** This method handles undo */
3137 void
3138 MidiRegionView::paste (framepos_t pos, float times, const MidiCutBuffer& mcb)
3139 {
3140         if (mcb.empty()) {
3141                 return;
3142         }
3143
3144         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("MIDI paste @ %1 times %2\n", pos, times));
3145
3146         trackview.session()->begin_reversible_command (_("paste"));
3147
3148         start_note_diff_command (_("paste"));
3149
3150         Evoral::MusicalTime beat_delta;
3151         Evoral::MusicalTime paste_pos_beats;
3152         Evoral::MusicalTime duration;
3153         Evoral::MusicalTime end_point = 0;
3154
3155         duration = (*mcb.notes().rbegin())->end_time() - (*mcb.notes().begin())->time();
3156         paste_pos_beats = region_frames_to_region_beats (pos - _region->position());
3157         beat_delta = (*mcb.notes().begin())->time() - paste_pos_beats;
3158         paste_pos_beats = 0;
3159
3160         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",
3161                                                        (*mcb.notes().begin())->time(),
3162                                                        (*mcb.notes().rbegin())->end_time(),
3163                                                        duration, pos, _region->position(),
3164                                                        paste_pos_beats, beat_delta));
3165
3166         clear_selection ();
3167
3168         for (int n = 0; n < (int) times; ++n) {
3169
3170                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3171
3172                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3173                         copied_note->set_time (paste_pos_beats + copied_note->time() - beat_delta);
3174
3175                         /* make all newly added notes selected */
3176
3177                         note_diff_add_note (copied_note, true);
3178                         end_point = copied_note->end_time();
3179                 }
3180
3181                 paste_pos_beats += duration;
3182         }
3183
3184         /* if we pasted past the current end of the region, extend the region */
3185
3186         framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3187         framepos_t region_end = _region->position() + _region->length() - 1;
3188
3189         if (end_frame > region_end) {
3190
3191                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3192
3193                 _region->clear_changes ();
3194                 _region->set_length (end_frame);
3195                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3196         }
3197
3198         apply_diff (true);
3199
3200         trackview.session()->commit_reversible_command ();
3201 }
3202
3203 struct EventNoteTimeEarlyFirstComparator {
3204         bool operator() (CanvasNoteEvent* a, CanvasNoteEvent* b) {
3205                 return a->note()->time() < b->note()->time();
3206         }
3207 };
3208
3209 void
3210 MidiRegionView::time_sort_events ()
3211 {
3212         if (!_sort_needed) {
3213                 return;
3214         }
3215
3216         EventNoteTimeEarlyFirstComparator cmp;
3217         _events.sort (cmp);
3218
3219         _sort_needed = false;
3220 }
3221
3222 void
3223 MidiRegionView::goto_next_note (bool add_to_selection)
3224 {
3225         bool use_next = false;
3226
3227         if (_events.back()->selected()) {
3228                 return;
3229         }
3230
3231         time_sort_events ();
3232
3233         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3234         uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3235
3236         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3237                 if ((*i)->selected()) {
3238                         use_next = true;
3239                         continue;
3240                 } else if (use_next) {
3241                         if (channel_mask & (1 << (*i)->note()->channel())) {
3242                                 if (!add_to_selection) {
3243                                         unique_select (*i);
3244                                 } else {
3245                                         note_selected (*i, true, false);
3246                                 }
3247                                 return;
3248                         }
3249                 }
3250         }
3251
3252         /* use the first one */
3253
3254         if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3255                 unique_select (_events.front());
3256         }
3257 }
3258
3259 void
3260 MidiRegionView::goto_previous_note (bool add_to_selection)
3261 {
3262         bool use_next = false;
3263
3264         if (_events.front()->selected()) {
3265                 return;
3266         }
3267
3268         time_sort_events ();
3269
3270         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3271         uint16_t const channel_mask = mtv->channel_selector().get_selected_channels ();
3272
3273         for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3274                 if ((*i)->selected()) {
3275                         use_next = true;
3276                         continue;
3277                 } else if (use_next) {
3278                         if (channel_mask & (1 << (*i)->note()->channel())) {
3279                                 if (!add_to_selection) {
3280                                         unique_select (*i);
3281                                 } else {
3282                                         note_selected (*i, true, false);
3283                                 }
3284                                 return;
3285                         }
3286                 }
3287         }
3288
3289         /* use the last one */
3290
3291         if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3292                 unique_select (*(_events.rbegin()));
3293         }
3294 }
3295
3296 void
3297 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3298 {
3299         bool had_selected = false;
3300
3301         time_sort_events ();
3302
3303         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3304                 if ((*i)->selected()) {
3305                         selected.insert ((*i)->note());
3306                         had_selected = true;
3307                 }
3308         }
3309
3310         if (allow_all_if_none_selected && !had_selected) {
3311                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3312                         selected.insert ((*i)->note());
3313                 }
3314         }
3315 }
3316
3317 void
3318 MidiRegionView::update_ghost_note (double x, double y)
3319 {
3320         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3321
3322         _last_ghost_x = x;
3323         _last_ghost_y = y;
3324
3325         _note_group->w2i (x, y);
3326
3327         PublicEditor& editor = trackview.editor ();
3328         
3329         framepos_t const unsnapped_frame = editor.pixel_to_frame (x);
3330         framecnt_t grid_frames;
3331         framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3332
3333         /* use region_frames... because we are converting a delta within the region
3334         */
3335          
3336         bool success;
3337         double length = editor.get_grid_type_as_beats (success, unsnapped_frame);
3338
3339         if (!success) {
3340                 length = 1;
3341         }
3342
3343         /* note that this sets the time of the ghost note in beats relative to
3344            the start of the source; that is how all note times are stored.
3345         */
3346         _ghost_note->note()->set_time (absolute_frames_to_source_beats (f + _region->position ()));
3347         _ghost_note->note()->set_length (length);
3348         _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3349         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3350
3351         /* the ghost note does not appear in ghost regions, so pass false in here */
3352         update_note (_ghost_note, false);
3353
3354         show_verbose_cursor (_ghost_note->note ());
3355 }
3356
3357 void
3358 MidiRegionView::create_ghost_note (double x, double y)
3359 {
3360         remove_ghost_note ();
3361
3362         boost::shared_ptr<NoteType> g (new NoteType);
3363         _ghost_note = new NoEventCanvasNote (*this, *_note_group, g);
3364         _ghost_note->property_outline_color_rgba() = 0x000000aa;
3365         update_ghost_note (x, y);
3366         _ghost_note->show ();
3367
3368         _last_ghost_x = x;
3369         _last_ghost_y = y;
3370
3371         show_verbose_cursor (_ghost_note->note ());
3372 }
3373
3374 void
3375 MidiRegionView::snap_changed ()
3376 {
3377         if (!_ghost_note) {
3378                 return;
3379         }
3380
3381         create_ghost_note (_last_ghost_x, _last_ghost_y);
3382 }
3383
3384 void
3385 MidiRegionView::drop_down_keys ()
3386 {
3387         _mouse_state = None;
3388 }
3389
3390 void
3391 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3392 {
3393         double note = midi_stream_view()->y_to_note(y);
3394         Events e;
3395         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3396
3397         uint16_t chn_mask = mtv->channel_selector().get_selected_channels();
3398
3399         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3400                 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3401         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3402                 get_events (e, Evoral::Sequence<Evoral::MusicalTime>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3403         } else {
3404                 return;
3405         }
3406
3407         bool add_mrv_selection = false;
3408
3409         if (_selection.empty()) {
3410                 add_mrv_selection = true;
3411         }
3412
3413         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3414                 if (_selection.insert (*i).second) {
3415                         (*i)->set_selected (true);
3416                 }
3417         }
3418
3419         if (add_mrv_selection) {
3420                 PublicEditor& editor (trackview.editor());
3421                 editor.get_selection().add (this);
3422         }
3423 }
3424
3425 void
3426 MidiRegionView::color_handler ()
3427 {
3428         RegionView::color_handler ();
3429
3430         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3431                 (*i)->set_selected ((*i)->selected()); // will change color
3432         }
3433
3434         /* XXX probably more to do here */
3435 }
3436
3437 void
3438 MidiRegionView::enable_display (bool yn)
3439 {
3440         RegionView::enable_display (yn);
3441         if (yn) {
3442                 redisplay_model ();
3443         }
3444 }
3445
3446 void
3447 MidiRegionView::show_step_edit_cursor (Evoral::MusicalTime pos)
3448 {
3449         if (_step_edit_cursor == 0) {
3450                 ArdourCanvas::Group* const group = (ArdourCanvas::Group*)get_canvas_group();
3451
3452                 _step_edit_cursor = new ArdourCanvas::SimpleRect (*group);
3453                 _step_edit_cursor->property_y1() = 0;
3454                 _step_edit_cursor->property_y2() = midi_stream_view()->contents_height();
3455                 _step_edit_cursor->property_fill_color_rgba() = RGBA_TO_UINT (45,0,0,90);
3456                 _step_edit_cursor->property_outline_color_rgba() = RGBA_TO_UINT (85,0,0,90);
3457         }
3458
3459         move_step_edit_cursor (pos);
3460         _step_edit_cursor->show ();
3461 }
3462
3463 void
3464 MidiRegionView::move_step_edit_cursor (Evoral::MusicalTime pos)
3465 {
3466         _step_edit_cursor_position = pos;
3467
3468         if (_step_edit_cursor) {
3469                 double pixel = trackview.editor().frame_to_pixel (region_beats_to_region_frames (pos));
3470                 _step_edit_cursor->property_x1() = pixel;
3471                 set_step_edit_cursor_width (_step_edit_cursor_width);
3472         }
3473 }
3474
3475 void
3476 MidiRegionView::hide_step_edit_cursor ()
3477 {
3478         if (_step_edit_cursor) {
3479                 _step_edit_cursor->hide ();
3480         }
3481 }
3482
3483 void
3484 MidiRegionView::set_step_edit_cursor_width (Evoral::MusicalTime beats)
3485 {
3486         _step_edit_cursor_width = beats;
3487
3488         if (_step_edit_cursor) {
3489                 _step_edit_cursor->property_x2() = _step_edit_cursor->property_x1() + trackview.editor().frame_to_pixel (region_beats_to_region_frames (beats));
3490         }
3491 }
3492
3493 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
3494  *  @param w Source that the data will end up in.
3495  */
3496 void
3497 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3498 {
3499         if (!_active_notes) {
3500                 /* we aren't actively being recorded to */
3501                 return;
3502         }
3503
3504         boost::shared_ptr<MidiSource> src = w.lock ();
3505         if (!src || src != midi_region()->midi_source()) {
3506                 /* recorded data was not destined for our source */
3507                 return;
3508         }
3509
3510         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3511
3512         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3513
3514         BeatsFramesConverter converter (trackview.session()->tempo_map(), mtv->midi_track()->get_capture_start_frame (0));
3515
3516         framepos_t back = max_framepos;
3517
3518         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3519                 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3520                 assert (ev.buffer ());
3521
3522                 /* ev.time() is in session frames, so (ev.time() - converter.origin_b()) is
3523                    frames from the start of the source, and so time_beats is in terms of the
3524                    source.
3525                 */
3526
3527                 Evoral::MusicalTime const time_beats = converter.from (ev.time () - converter.origin_b ());
3528
3529                 if (ev.type() == MIDI_CMD_NOTE_ON) {
3530
3531                         boost::shared_ptr<NoteType> note (
3532                                 new NoteType (ev.channel(), time_beats, 0, ev.note(), ev.velocity())
3533                                                           );
3534
3535                         add_note (note, true);
3536
3537                         /* fix up our note range */
3538                         if (ev.note() < _current_range_min) {
3539                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3540                         } else if (ev.note() > _current_range_max) {
3541                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3542                         }
3543
3544                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3545                         resolve_note (ev.note (), time_beats);
3546                 }
3547
3548                 back = ev.time ();
3549         }
3550
3551         midi_stream_view()->check_record_layers (region(), back);
3552 }
3553
3554 void
3555 MidiRegionView::trim_front_starting ()
3556 {
3557         /* Reparent the note group to the region view's parent, so that it doesn't change
3558            when the region view is trimmed.
3559         */
3560         _temporary_note_group = new ArdourCanvas::Group (*group->property_parent ());
3561         _temporary_note_group->move (group->property_x(), group->property_y());
3562         _note_group->reparent (*_temporary_note_group);
3563 }
3564
3565 void
3566 MidiRegionView::trim_front_ending ()
3567 {
3568         _note_group->reparent (*group);
3569         delete _temporary_note_group;
3570         _temporary_note_group = 0;
3571
3572         if (_region->start() < 0) {
3573                 /* Trim drag made start time -ve; fix this */
3574                 midi_region()->fix_negative_start ();
3575         }
3576 }
3577
3578 void
3579 MidiRegionView::edit_patch_change (ArdourCanvas::CanvasPatchChange* pc)
3580 {
3581         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), Gtk::Stock::APPLY);
3582         if (d.run () != Gtk::RESPONSE_ACCEPT) {
3583                 return;
3584         }
3585
3586         change_patch_change (pc->patch(), d.patch ());
3587 }
3588
3589
3590 void
3591 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3592 {
3593         char buf[24];
3594         snprintf (buf, sizeof (buf), "%s (%d) Chn %d\nVel %d",
3595                   Evoral::midi_note_name (n->note()).c_str(),
3596                   (int) n->note (),
3597                   (int) n->channel() + 1,
3598                   (int) n->velocity());
3599
3600         show_verbose_cursor (buf, 10, 20);
3601 }
3602
3603 void
3604 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3605 {
3606         double wx, wy;
3607
3608         trackview.editor().get_pointer_position (wx, wy);
3609
3610         wx += xoffset;
3611         wy += yoffset;
3612
3613         /* Flip the cursor above the mouse pointer if it would overlap the bottom of the canvas */
3614
3615         double x1, y1, x2, y2;
3616         trackview.editor().verbose_cursor()->canvas_item()->get_bounds (x1, y1, x2, y2);
3617
3618         if ((wy + y2 - y1) > trackview.editor().canvas_height()) {
3619                 wy -= (y2 - y1) + 2 * yoffset;
3620         }
3621
3622         trackview.editor().verbose_cursor()->set (text, wx, wy);
3623         trackview.editor().verbose_cursor()->show ();
3624 }
3625
3626 /** @param p A session framepos.
3627  *  @param grid_frames Filled in with the number of frames that a grid interval is at p.
3628  *  @return p snapped to the grid subdivision underneath it.
3629  */
3630 framepos_t
3631 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
3632 {
3633         PublicEditor& editor = trackview.editor ();
3634         
3635         bool success;
3636         Evoral::MusicalTime grid_beats = editor.get_grid_type_as_beats (success, p);
3637
3638         if (!success) {
3639                 grid_beats = 1;
3640         }
3641         
3642         grid_frames = region_beats_to_region_frames (grid_beats);
3643
3644         /* Hack so that we always snap to the note that we are over, instead of snapping
3645            to the next one if we're more than halfway through the one we're over.
3646         */
3647         if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
3648                 p -= grid_frames / 2;
3649         }
3650
3651         return snap_frame_to_frame (p);
3652 }
3653
3654 /** Called when the selection has been cleared in any MidiRegionView.
3655  *  @param rv MidiRegionView that the selection was cleared in.
3656  */
3657 void
3658 MidiRegionView::selection_cleared (MidiRegionView* rv)
3659 {
3660         if (rv == this) {
3661                 return;
3662         }
3663
3664         /* Clear our selection in sympathy; but don't signal the fact */
3665         clear_selection (false);
3666 }