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