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