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