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