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