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