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