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