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