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