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