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