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