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