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