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