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