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