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