Tempo ramps - rename meter_at() -> meter_at_frame() & make the ticker slightly more...
[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); // calls RegionView::set_duration()
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                 /* reset_width dependent_items() redisplays model */
1410         }
1411
1412         if (what_changed.contains (ARDOUR::Properties::start) ||
1413             what_changed.contains (ARDOUR::Properties::position)) {
1414                 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1415         }
1416 }
1417
1418 void
1419 MidiRegionView::reset_width_dependent_items (double pixel_width)
1420 {
1421         RegionView::reset_width_dependent_items(pixel_width);
1422
1423         if (_enable_display) {
1424                 redisplay_model();
1425         }
1426
1427         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1428                 if ((*x)->canvas_item()->width() >= _pixel_width) {
1429                         (*x)->hide();
1430                 } else {
1431                         (*x)->show();
1432                 }
1433         }
1434
1435         move_step_edit_cursor (_step_edit_cursor_position);
1436         set_step_edit_cursor_width (_step_edit_cursor_width);
1437 }
1438
1439 void
1440 MidiRegionView::set_height (double height)
1441 {
1442         double old_height = _height;
1443         RegionView::set_height(height);
1444
1445         apply_note_range (midi_stream_view()->lowest_note(),
1446                           midi_stream_view()->highest_note(),
1447                           height != old_height);
1448
1449         if (name_text) {
1450                 name_text->raise_to_top();
1451         }
1452
1453         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1454                 (*x)->set_height (midi_stream_view()->contents_height());
1455         }
1456
1457         if (_step_edit_cursor) {
1458                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1459         }
1460 }
1461
1462
1463 /** Apply the current note range from the stream view
1464  * by repositioning/hiding notes as necessary
1465  */
1466 void
1467 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1468 {
1469         if (!_enable_display) {
1470                 return;
1471         }
1472
1473         if (!force && _current_range_min == min && _current_range_max == max) {
1474                 return;
1475         }
1476
1477         _current_range_min = min;
1478         _current_range_max = max;
1479
1480         for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1481                 NoteBase* event = *i;
1482                 boost::shared_ptr<NoteType> note (event->note());
1483
1484                 if (note->note() < _current_range_min ||
1485                     note->note() > _current_range_max) {
1486                         event->hide();
1487                 } else {
1488                         event->show();
1489                 }
1490
1491                 if (Note* cnote = dynamic_cast<Note*>(event)) {
1492
1493                         const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1494                         const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1495
1496                         if (y0 < 0 || y1 >= _height) {
1497                                 /* During DnD, the region uses the 'old/current'
1498                                  * midi_stream_view()'s range and its position/height calculation.
1499                                  *
1500                                  * Ideally DnD would decouple the midi_stream_view() for the
1501                                  * region(s) being dragged and set it to the target's range
1502                                  * (or in case of the drop-zone, FullRange).
1503                                  * but I don't see how this can be done without major rework.
1504                                  *
1505                                  * For now, just prevent visual bleeding of events in case
1506                                  * the target-track is smaller.
1507                                  */
1508                                 event->hide();
1509                                 continue;
1510                         }
1511                         cnote->set_y0 (y0);
1512                         cnote->set_y1 (y1);
1513
1514                 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1515                         update_hit (chit);
1516                 }
1517         }
1518 }
1519
1520 GhostRegion*
1521 MidiRegionView::add_ghost (TimeAxisView& tv)
1522 {
1523         double unit_position = _region->position () / samples_per_pixel;
1524         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1525         MidiGhostRegion* ghost;
1526
1527         if (mtv && mtv->midi_view()) {
1528                 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1529                    to allow having midi notes on top of note lines and waveforms.
1530                 */
1531                 ghost = new MidiGhostRegion (*this, *mtv->midi_view(), trackview, unit_position);
1532         } else {
1533                 ghost = new MidiGhostRegion (*this, tv, trackview, unit_position);
1534         }
1535
1536         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1537                 ghost->add_note(*i);
1538         }
1539
1540         ghost->set_height ();
1541         ghost->set_duration (_region->length() / samples_per_pixel);
1542         ghosts.push_back (ghost);
1543
1544         return ghost;
1545 }
1546
1547
1548 /** Begin tracking note state for successive calls to add_event
1549  */
1550 void
1551 MidiRegionView::begin_write()
1552 {
1553         if (_active_notes) {
1554                 delete[] _active_notes;
1555         }
1556         _active_notes = new Note*[128];
1557         for (unsigned i = 0; i < 128; ++i) {
1558                 _active_notes[i] = 0;
1559         }
1560 }
1561
1562
1563 /** Destroy note state for add_event
1564  */
1565 void
1566 MidiRegionView::end_write()
1567 {
1568         delete[] _active_notes;
1569         _active_notes = 0;
1570         _marked_for_selection.clear();
1571         _marked_for_velocity.clear();
1572 }
1573
1574
1575 /** Resolve an active MIDI note (while recording).
1576  */
1577 void
1578 MidiRegionView::resolve_note(uint8_t note, Evoral::Beats end_time)
1579 {
1580         if (midi_view()->note_mode() != Sustained) {
1581                 return;
1582         }
1583
1584         if (_active_notes && _active_notes[note]) {
1585                 /* Set note length so update_note() works.  Note this is a local note
1586                    for recording, not from a model, so we can safely mess with it. */
1587                 _active_notes[note]->note()->set_length(
1588                         end_time - _active_notes[note]->note()->time());
1589
1590                 /* End time is relative to the region being recorded. */
1591                 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1592
1593                 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1594                 _active_notes[note]->set_outline_all ();
1595                 _active_notes[note] = 0;
1596         }
1597 }
1598
1599
1600 /** Extend active notes to rightmost edge of region (if length is changed)
1601  */
1602 void
1603 MidiRegionView::extend_active_notes()
1604 {
1605         if (!_active_notes) {
1606                 return;
1607         }
1608
1609         for (unsigned i = 0; i < 128; ++i) {
1610                 if (_active_notes[i]) {
1611                         _active_notes[i]->set_x1(
1612                                 trackview.editor().sample_to_pixel(_region->length()));
1613                 }
1614         }
1615 }
1616
1617 void
1618 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1619 {
1620         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1621                 return;
1622         }
1623
1624         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1625
1626         if (!route_ui || !route_ui->midi_track()) {
1627                 return;
1628         }
1629
1630         NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1631         np->add (note);
1632         np->play ();
1633
1634         /* NotePlayer deletes itself */
1635 }
1636
1637 void
1638 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1639 {
1640         const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1641         start_playing_midi_chord(notes);
1642 }
1643
1644 void
1645 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1646 {
1647         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1648                 return;
1649         }
1650
1651         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1652
1653         if (!route_ui || !route_ui->midi_track()) {
1654                 return;
1655         }
1656
1657         NotePlayer* player = new NotePlayer (route_ui->midi_track());
1658
1659         for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1660                 player->add (*n);
1661         }
1662
1663         player->play ();
1664 }
1665
1666
1667 bool
1668 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1669 {
1670         /* This is imprecise due to all the conversion conversion involved, so only
1671            hide notes if they seem to start more than one tick before the start. */
1672         const framecnt_t tick_frames       = Evoral::Beats::tick().to_ticks(trackview.session()->frame_rate());
1673         const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1674         const bool       outside           = ((note_start_frames <= -tick_frames) ||
1675                                               (note_start_frames >= _region->length()));
1676
1677         visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1678                 (note->note() <= midi_stream_view()->highest_note());
1679
1680         return !outside;
1681 }
1682
1683 void
1684 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1685 {
1686         Note* sus = NULL;
1687         Hit*  hit = NULL;
1688         if ((sus = dynamic_cast<Note*>(note))) {
1689                 update_sustained(sus, update_ghost_regions);
1690         } else if ((hit = dynamic_cast<Hit*>(note))) {
1691                 update_hit(hit, update_ghost_regions);
1692         }
1693 }
1694
1695 /** Update a canvas note's size from its model note.
1696  *  @param ev Canvas note to update.
1697  *  @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1698  */
1699 void
1700 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1701 {
1702         boost::shared_ptr<NoteType> note = ev->note();
1703         const double x0 = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1704         double x1;
1705         const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1706         double y1;
1707
1708         /* trim note display to not overlap the end of its region */
1709
1710         if (note->length() > 0) {
1711                 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1712                 x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1;
1713         } else {
1714                 x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
1715         }
1716
1717         y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1);
1718
1719         ev->set_x0 (x0);
1720         ev->set_x1 (x1);
1721         ev->set_y0 (y0);
1722         ev->set_y1 (y1);
1723
1724         if (!note->length()) {
1725                 if (_active_notes && note->note() < 128) {
1726                         Note* const old_rect = _active_notes[note->note()];
1727                         if (old_rect) {
1728                                 /* There is an active note on this key, so we have a stuck
1729                                    note.  Finish the old rectangle here. */
1730                                 old_rect->set_x1 (x1);
1731                                 old_rect->set_outline_all ();
1732                         }
1733                         _active_notes[note->note()] = ev;
1734                 }
1735                 /* outline all but right edge */
1736                 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1737                                               ArdourCanvas::Rectangle::TOP|
1738                                               ArdourCanvas::Rectangle::LEFT|
1739                                               ArdourCanvas::Rectangle::BOTTOM));
1740         } else {
1741                 /* outline all edges */
1742                 ev->set_outline_all ();
1743         }
1744
1745         // Update color in case velocity has changed
1746         //ev->set_fill_color(ev->base_color());
1747         //ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1748
1749         if (update_ghost_regions) {
1750                 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1751                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1752                         if (gr) {
1753                                 gr->update_note (ev);
1754                         }
1755                 }
1756         }
1757 }
1758
1759 void
1760 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1761 {
1762         boost::shared_ptr<NoteType> note = ev->note();
1763
1764         const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1765         const double x = trackview.editor().sample_to_pixel(note_start_frames);
1766         const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1767         const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1768
1769         // see DnD note in MidiRegionView::apply_note_range() above
1770         if (y <= 0 || y >= _height) {
1771                 ev->hide();
1772         } else {
1773                 ev->show();
1774         }
1775
1776         ev->set_position (ArdourCanvas::Duple (x, y));
1777         ev->set_height (diamond_size);
1778
1779         // Update color in case velocity has changed
1780         ev->set_fill_color(ev->base_color());
1781         ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1782
1783         if (update_ghost_regions) {
1784                 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1785                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1786                         if (gr) {
1787                                 gr->update_note (ev);
1788                         }
1789                 }
1790         }
1791 }
1792
1793 /** Add a MIDI note to the view (with length).
1794  *
1795  * If in sustained mode, notes with length 0 will be considered active
1796  * notes, and resolve_note should be called when the corresponding note off
1797  * event arrives, to properly display the note.
1798  */
1799 NoteBase*
1800 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1801 {
1802         NoteBase* event = 0;
1803
1804         if (midi_view()->note_mode() == Sustained) {
1805
1806                 Note* ev_rect = new Note (*this, _note_group, note);
1807
1808                 update_sustained (ev_rect);
1809
1810                 event = ev_rect;
1811
1812         } else if (midi_view()->note_mode() == Percussive) {
1813
1814                 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1815
1816                 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1817
1818                 update_hit (ev_diamond);
1819
1820                 event = ev_diamond;
1821
1822         } else {
1823                 event = 0;
1824         }
1825
1826         if (event) {
1827                 MidiGhostRegion* gr;
1828
1829                 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1830                         if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1831                                 gr->add_note(event);
1832                         }
1833                 }
1834
1835                 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1836                         note_selected(event, true);
1837                 }
1838
1839                 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1840                         event->show_velocity();
1841                 }
1842
1843                 event->on_channel_selection_change (get_selected_channels());
1844                 _events.push_back(event);
1845
1846                 if (visible) {
1847                         event->show();
1848                 } else {
1849                         event->hide ();
1850                 }
1851         }
1852
1853         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1854         MidiStreamView* const view = mtv->midi_view();
1855
1856         view->update_note_range (note->note());
1857         return event;
1858 }
1859
1860 void
1861 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1862                                Evoral::Beats pos, Evoral::Beats len)
1863 {
1864         boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1865
1866         /* potentially extend region to hold new note */
1867
1868         framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1869         framepos_t region_end = _region->last_frame();
1870
1871         if (end_frame > region_end) {
1872                 _region->set_length (end_frame - _region->position());
1873         }
1874
1875         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1876         MidiStreamView* const view = mtv->midi_view();
1877
1878         view->update_note_range(new_note->note());
1879
1880         _marked_for_selection.clear ();
1881
1882         start_note_diff_command (_("step add"));
1883
1884         clear_editor_note_selection ();
1885         note_diff_add_note (new_note, true, false);
1886
1887         apply_diff();
1888
1889         // last_step_edit_note = new_note;
1890 }
1891
1892 void
1893 MidiRegionView::step_sustain (Evoral::Beats beats)
1894 {
1895         change_note_lengths (false, false, beats, false, true);
1896 }
1897
1898 /** Add a new patch change flag to the canvas.
1899  * @param patch the patch change to add
1900  * @param the text to display in the flag
1901  * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1902  */
1903 void
1904 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1905 {
1906         framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1907         const double x = trackview.editor().sample_to_pixel (region_frames);
1908
1909         double const height = midi_stream_view()->contents_height();
1910
1911         // CAIROCANVAS: active_channel info removed from PatcChange constructor
1912         // so we need to do something more sophisticated to keep its color
1913         // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1914         // up to date.
1915
1916         boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1917                 new PatchChange(*this, group,
1918                                 displaytext,
1919                                 height,
1920                                 x, 1.0,
1921                                 instrument_info(),
1922                                 patch));
1923
1924         if (patch_change->item().width() < _pixel_width) {
1925                 // Show unless patch change is beyond the region bounds
1926                 if (region_frames < 0 || region_frames >= _region->length()) {
1927                         patch_change->hide();
1928                 } else {
1929                         patch_change->show();
1930                 }
1931         } else {
1932                 patch_change->hide ();
1933         }
1934
1935         _patch_changes.push_back (patch_change);
1936 }
1937
1938 MIDI::Name::PatchPrimaryKey
1939 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1940 {
1941         return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1942 }
1943
1944 /// Return true iff @p pc applies to the given time on the given channel.
1945 static bool
1946 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::Beats time, uint8_t channel)
1947 {
1948         return pc->time() <= time && pc->channel() == channel;
1949 }
1950
1951 void
1952 MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1953 {
1954         // The earliest event not before time
1955         MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1956
1957         // Go backwards until we find the latest PC for this channel, or the start
1958         while (i != _model->patch_changes().begin() &&
1959                (i == _model->patch_changes().end() ||
1960                 !patch_applies(*i, time, channel))) {
1961                 --i;
1962         }
1963
1964         if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1965                 key.set_bank((*i)->bank());
1966                 key.set_program((*i)->program ());
1967         } else {
1968                 key.set_bank(0);
1969                 key.set_program(0);
1970         }
1971 }
1972
1973 void
1974 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1975 {
1976         string name = _("alter patch change");
1977         trackview.editor().begin_reversible_command (name);
1978         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
1979
1980         if (pc.patch()->program() != new_patch.program()) {
1981                 c->change_program (pc.patch (), new_patch.program());
1982         }
1983
1984         int const new_bank = new_patch.bank();
1985         if (pc.patch()->bank() != new_bank) {
1986                 c->change_bank (pc.patch (), new_bank);
1987         }
1988
1989         _model->apply_command (*trackview.session(), c);
1990         trackview.editor().commit_reversible_command ();
1991
1992         _patch_changes.clear ();
1993         display_patch_changes ();
1994 }
1995
1996 void
1997 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change)
1998 {
1999         string name = _("alter patch change");
2000         trackview.editor().begin_reversible_command (name);
2001         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2002
2003         if (old_change->time() != new_change.time()) {
2004                 c->change_time (old_change, new_change.time());
2005         }
2006
2007         if (old_change->channel() != new_change.channel()) {
2008                 c->change_channel (old_change, new_change.channel());
2009         }
2010
2011         if (old_change->program() != new_change.program()) {
2012                 c->change_program (old_change, new_change.program());
2013         }
2014
2015         if (old_change->bank() != new_change.bank()) {
2016                 c->change_bank (old_change, new_change.bank());
2017         }
2018
2019         _model->apply_command (*trackview.session(), c);
2020         trackview.editor().commit_reversible_command ();
2021
2022         _patch_changes.clear ();
2023         display_patch_changes ();
2024 }
2025
2026 /** Add a patch change to the region.
2027  *  @param t Time in frames relative to region position
2028  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2029  *  MidiTimeAxisView::get_channel_for_add())
2030  */
2031 void
2032 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
2033 {
2034         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2035         string name = _("add patch change");
2036
2037         trackview.editor().begin_reversible_command (name);
2038         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2039         c->add (MidiModel::PatchChangePtr (
2040                         new Evoral::PatchChange<Evoral::Beats> (
2041                                 absolute_frames_to_source_beats (_region->position() + t),
2042                                 mtv->get_channel_for_add(), patch.program(), patch.bank()
2043                                 )
2044                         )
2045                 );
2046
2047         _model->apply_command (*trackview.session(), c);
2048         trackview.editor().commit_reversible_command ();
2049
2050         _patch_changes.clear ();
2051         display_patch_changes ();
2052 }
2053
2054 void
2055 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
2056 {
2057         trackview.editor().begin_reversible_command (_("move patch change"));
2058         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2059         c->change_time (pc.patch (), t);
2060         _model->apply_command (*trackview.session(), c);
2061         trackview.editor().commit_reversible_command ();
2062
2063         _patch_changes.clear ();
2064         display_patch_changes ();
2065 }
2066
2067 void
2068 MidiRegionView::delete_patch_change (PatchChange* pc)
2069 {
2070         trackview.editor().begin_reversible_command (_("delete patch change"));
2071         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2072         c->remove (pc->patch ());
2073         _model->apply_command (*trackview.session(), c);
2074         trackview.editor().commit_reversible_command ();
2075
2076         _patch_changes.clear ();
2077         display_patch_changes ();
2078 }
2079
2080 void
2081 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2082 {
2083         MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2084         if (bank) {
2085                 key.set_bank(key.bank() + delta);
2086         } else {
2087                 key.set_program(key.program() + delta);
2088         }
2089         change_patch_change(patch, key);
2090 }
2091
2092 void
2093 MidiRegionView::note_deleted (NoteBase* cne)
2094 {
2095         if (_selection.empty()) {
2096                 return;
2097         }
2098
2099         _selection.erase (cne);
2100 }
2101
2102 void
2103 MidiRegionView::delete_selection()
2104 {
2105         if (_selection.empty()) {
2106                 return;
2107         }
2108
2109         start_note_diff_command (_("delete selection"));
2110
2111         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2112                 if ((*i)->selected()) {
2113                         _note_diff_command->remove((*i)->note());
2114                 }
2115         }
2116
2117         _selection.clear();
2118
2119         apply_diff ();
2120         hide_verbose_cursor ();
2121 }
2122
2123 void
2124 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2125 {
2126         start_note_diff_command (_("delete note"));
2127         _note_diff_command->remove (n);
2128         apply_diff ();
2129
2130         hide_verbose_cursor ();
2131 }
2132
2133 void
2134 MidiRegionView::clear_editor_note_selection ()
2135 {
2136         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_editor_note_selection\n");
2137         PublicEditor& editor(trackview.editor());
2138         editor.get_selection().clear_midi_notes();
2139 }
2140
2141 void
2142 MidiRegionView::clear_selection ()
2143 {
2144         clear_selection_internal();
2145         PublicEditor& editor(trackview.editor());
2146         editor.get_selection().remove(this);
2147 }
2148
2149 void
2150 MidiRegionView::clear_selection_internal ()
2151 {
2152         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_selection_internal\n");
2153
2154         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2155                 (*i)->set_selected(false);
2156                 (*i)->hide_velocity();
2157         }
2158         _selection.clear();
2159
2160         if (_entered) {
2161                 // Clearing selection entirely, ungrab keyboard
2162                 Keyboard::magic_widget_drop_focus();
2163                 _grabbed_keyboard = false;
2164         }
2165 }
2166
2167 void
2168 MidiRegionView::unique_select(NoteBase* ev)
2169 {
2170         clear_editor_note_selection();
2171         add_to_selection(ev);
2172 }
2173
2174 void
2175 MidiRegionView::select_all_notes ()
2176 {
2177         clear_editor_note_selection ();
2178
2179         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2180                 add_to_selection (*i);
2181         }
2182 }
2183
2184 void
2185 MidiRegionView::select_range (framepos_t start, framepos_t end)
2186 {
2187         clear_editor_note_selection ();
2188
2189         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2190                 framepos_t t = source_beats_to_absolute_frames((*i)->note()->time());
2191                 if (t >= start && t <= end) {
2192                         add_to_selection (*i);
2193                 }
2194         }
2195 }
2196
2197 void
2198 MidiRegionView::invert_selection ()
2199 {
2200         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2201                 if ((*i)->selected()) {
2202                         remove_from_selection(*i);
2203                 } else {
2204                         add_to_selection (*i);
2205                 }
2206         }
2207 }
2208
2209 /** Used for selection undo/redo.
2210     The requested notes most likely won't exist in the view until the next model redisplay.
2211 */
2212 void
2213 MidiRegionView::select_notes (list<boost::shared_ptr<NoteType> > notes)
2214 {
2215         NoteBase* cne;
2216         list<boost::shared_ptr<NoteType> >::iterator n;
2217
2218         for (n = notes.begin(); n != notes.end(); ++n) {
2219                 if ((cne = find_canvas_note(*(*n))) != 0) {
2220                         add_to_selection (cne);
2221                 } else {
2222                         _pending_note_selection.insert(*n);
2223                 }
2224         }
2225 }
2226
2227 void
2228 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2229 {
2230         bool have_selection = !_selection.empty();
2231         uint8_t low_note = 127;
2232         uint8_t high_note = 0;
2233         MidiModel::Notes& notes (_model->notes());
2234         _optimization_iterator = _events.begin();
2235
2236         if (extend && !have_selection) {
2237                 extend = false;
2238         }
2239
2240         /* scan existing selection to get note range */
2241
2242         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2243                 if ((*i)->note()->note() < low_note) {
2244                         low_note = (*i)->note()->note();
2245                 }
2246                 if ((*i)->note()->note() > high_note) {
2247                         high_note = (*i)->note()->note();
2248                 }
2249         }
2250
2251         if (!add) {
2252                 clear_editor_note_selection ();
2253
2254                 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2255                         /* only note previously selected is the one we are
2256                          * reselecting. treat this as cancelling the selection.
2257                          */
2258                         return;
2259                 }
2260         }
2261
2262         if (extend) {
2263                 low_note = min (low_note, notenum);
2264                 high_note = max (high_note, notenum);
2265         }
2266
2267         _no_sound_notes = true;
2268
2269         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2270
2271                 boost::shared_ptr<NoteType> note (*n);
2272                 NoteBase* cne;
2273                 bool select = false;
2274
2275                 if (((1 << note->channel()) & channel_mask) != 0) {
2276                         if (extend) {
2277                                 if ((note->note() >= low_note && note->note() <= high_note)) {
2278                                         select = true;
2279                                 }
2280                         } else if (note->note() == notenum) {
2281                                 select = true;
2282                         }
2283                 }
2284
2285                 if (select) {
2286                         if ((cne = find_canvas_note (note)) != 0) {
2287                                 // extend is false because we've taken care of it,
2288                                 // since it extends by time range, not pitch.
2289                                 note_selected (cne, add, false);
2290                         }
2291                 }
2292
2293                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2294
2295         }
2296
2297         _no_sound_notes = false;
2298 }
2299
2300 void
2301 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2302 {
2303         MidiModel::Notes& notes (_model->notes());
2304         _optimization_iterator = _events.begin();
2305
2306         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2307
2308                 boost::shared_ptr<NoteType> note (*n);
2309                 NoteBase* cne;
2310
2311                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2312                         if ((cne = find_canvas_note (note)) != 0) {
2313                                 if (cne->selected()) {
2314                                         note_deselected (cne);
2315                                 } else {
2316                                         note_selected (cne, true, false);
2317                                 }
2318                         }
2319                 }
2320         }
2321 }
2322
2323 void
2324 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2325 {
2326         if (!add) {
2327                 clear_editor_note_selection();
2328                 add_to_selection (ev);
2329         }
2330
2331         if (!extend) {
2332
2333                 if (!ev->selected()) {
2334                         add_to_selection (ev);
2335                 }
2336
2337         } else {
2338                 /* find end of latest note selected, select all between that and the start of "ev" */
2339
2340                 Evoral::Beats earliest = Evoral::MaxBeats;
2341                 Evoral::Beats latest   = Evoral::Beats();
2342
2343                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2344                         if ((*i)->note()->end_time() > latest) {
2345                                 latest = (*i)->note()->end_time();
2346                         }
2347                         if ((*i)->note()->time() < earliest) {
2348                                 earliest = (*i)->note()->time();
2349                         }
2350                 }
2351
2352                 if (ev->note()->end_time() > latest) {
2353                         latest = ev->note()->end_time();
2354                 }
2355
2356                 if (ev->note()->time() < earliest) {
2357                         earliest = ev->note()->time();
2358                 }
2359
2360                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2361
2362                         /* find notes entirely within OR spanning the earliest..latest range */
2363
2364                         if (((*i)->note()->time() >= earliest && (*i)->note()->end_time() <= latest) ||
2365                             ((*i)->note()->time() <= earliest && (*i)->note()->end_time() >= latest)) {
2366                                 add_to_selection (*i);
2367                         }
2368
2369                 }
2370         }
2371 }
2372
2373 void
2374 MidiRegionView::note_deselected(NoteBase* ev)
2375 {
2376         remove_from_selection (ev);
2377 }
2378
2379 void
2380 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2381 {
2382         PublicEditor& editor = trackview.editor();
2383
2384         // Convert to local coordinates
2385         const framepos_t p  = _region->position();
2386         const double     y  = midi_view()->y_position();
2387         const double     x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2388         const double     x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2389         const double     y0 = max(0.0, gy0 - y);
2390         const double     y1 = max(0.0, gy1 - y);
2391
2392         // TODO: Make this faster by storing the last updated selection rect, and only
2393         // adjusting things that are in the area that appears/disappeared.
2394         // We probably need a tree to be able to find events in O(log(n)) time.
2395
2396         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2397                 if ((*i)->x0() < x1 && (*i)->x1() > x0 && (*i)->y0() < y1 && (*i)->y1() > y0) {
2398                         // Rectangles intersect
2399                         if (!(*i)->selected()) {
2400                                 add_to_selection (*i);
2401                         }
2402                 } else if ((*i)->selected() && !extend) {
2403                         // Rectangles do not intersect
2404                         remove_from_selection (*i);
2405                 }
2406         }
2407
2408         typedef RouteTimeAxisView::AutomationTracks ATracks;
2409         typedef std::list<Selectable*>              Selectables;
2410
2411         /* Add control points to selection. */
2412         const ATracks& atracks = midi_view()->automation_tracks();
2413         Selectables    selectables;
2414         editor.get_selection().clear_points();
2415         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2416                 a->second->get_selectables(start, end, gy0, gy1, selectables);
2417                 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2418                         ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2419                         if (cp) {
2420                                 editor.get_selection().add(cp);
2421                         }
2422                 }
2423                 a->second->set_selected_points(editor.get_selection().points);
2424         }
2425 }
2426
2427 void
2428 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2429 {
2430         if (y1 > y2) {
2431                 swap (y1, y2);
2432         }
2433
2434         // TODO: Make this faster by storing the last updated selection rect, and only
2435         // adjusting things that are in the area that appears/disappeared.
2436         // We probably need a tree to be able to find events in O(log(n)) time.
2437
2438         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2439                 if (((*i)->y1() >= y1 && (*i)->y1() <= y2)) {
2440                         // within y- (note-) range
2441                         if (!(*i)->selected()) {
2442                                 add_to_selection (*i);
2443                         }
2444                 } else if ((*i)->selected() && !extend) {
2445                         remove_from_selection (*i);
2446                 }
2447         }
2448 }
2449
2450 void
2451 MidiRegionView::remove_from_selection (NoteBase* ev)
2452 {
2453         Selection::iterator i = _selection.find (ev);
2454
2455         if (i != _selection.end()) {
2456                 _selection.erase (i);
2457                 if (_selection.empty() && _grabbed_keyboard) {
2458                         // Ungrab keyboard
2459                         Keyboard::magic_widget_drop_focus();
2460                         _grabbed_keyboard = false;
2461                 }
2462         }
2463
2464         ev->set_selected (false);
2465         ev->hide_velocity ();
2466
2467         if (_selection.empty()) {
2468                 PublicEditor& editor (trackview.editor());
2469                 editor.get_selection().remove (this);
2470         }
2471 }
2472
2473 void
2474 MidiRegionView::add_to_selection (NoteBase* ev)
2475 {
2476         const bool selection_was_empty = _selection.empty();
2477
2478         if (_selection.insert (ev).second) {
2479                 ev->set_selected (true);
2480                 start_playing_midi_note ((ev)->note());
2481                 if (selection_was_empty && _entered) {
2482                         // Grab keyboard for moving notes with arrow keys
2483                         Keyboard::magic_widget_grab_focus();
2484                         _grabbed_keyboard = true;
2485                 }
2486         }
2487
2488         if (selection_was_empty) {
2489                 PublicEditor& editor (trackview.editor());
2490                 editor.get_selection().add (this);
2491         }
2492 }
2493
2494 void
2495 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2496 {
2497         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2498         PossibleChord to_play;
2499         Evoral::Beats earliest = Evoral::MaxBeats;
2500
2501         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2502                 if ((*i)->note()->time() < earliest) {
2503                         earliest = (*i)->note()->time();
2504                 }
2505         }
2506
2507         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2508                 if ((*i)->note()->time() == earliest) {
2509                         to_play.push_back ((*i)->note());
2510                 }
2511                 (*i)->move_event(dx, dy);
2512         }
2513
2514         if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2515
2516                 if (to_play.size() > 1) {
2517
2518                         PossibleChord shifted;
2519
2520                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2521                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2522                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2523                                 shifted.push_back (moved_note);
2524                         }
2525
2526                         start_playing_midi_chord (shifted);
2527
2528                 } else if (!to_play.empty()) {
2529
2530                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2531                         moved_note->set_note (moved_note->note() + cumulative_dy);
2532                         start_playing_midi_note (moved_note);
2533                 }
2534         }
2535 }
2536
2537 void
2538 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote)
2539 {
2540         uint8_t lowest_note_in_selection  = 127;
2541         uint8_t highest_note_in_selection = 0;
2542         uint8_t highest_note_difference   = 0;
2543
2544         // find highest and lowest notes first
2545
2546         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2547                 uint8_t pitch = (*i)->note()->note();
2548                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2549                 highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2550         }
2551
2552         /*
2553           cerr << "dnote: " << (int) dnote << endl;
2554           cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2555           << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2556           cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2557           << int(highest_note_in_selection) << endl;
2558           cerr << "selection size: " << _selection.size() << endl;
2559           cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2560         */
2561
2562         // Make sure the note pitch does not exceed the MIDI standard range
2563         if (highest_note_in_selection + dnote > 127) {
2564                 highest_note_difference = highest_note_in_selection - 127;
2565         }
2566
2567         start_note_diff_command (_("move notes"));
2568
2569         for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2570
2571                 framepos_t new_frames = source_beats_to_absolute_frames ((*i)->note()->time()) + dt;
2572                 Evoral::Beats new_time = absolute_frames_to_source_beats (new_frames);
2573
2574                 if (new_time < 0) {
2575                         continue;
2576                 }
2577
2578                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2579
2580                 uint8_t original_pitch = (*i)->note()->note();
2581                 uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2582
2583                 // keep notes in standard midi range
2584                 clamp_to_0_127(new_pitch);
2585
2586                 lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2587                 highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2588
2589                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2590         }
2591
2592         apply_diff();
2593
2594         // care about notes being moved beyond the upper/lower bounds on the canvas
2595         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
2596             highest_note_in_selection > midi_stream_view()->highest_note()) {
2597                 midi_stream_view()->set_note_range(MidiStreamView::ContentsRange);
2598         }
2599 }
2600
2601 /** @param x Pixel relative to the region position.
2602  *  @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
2603  *  Used for inverting the snap logic with key modifiers and snap delta calculation.
2604  *  @return Snapped frame relative to the region position.
2605  */
2606 framepos_t
2607 MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
2608 {
2609         PublicEditor& editor (trackview.editor());
2610         return snap_frame_to_frame (editor.pixel_to_sample (x), ensure_snap);
2611 }
2612
2613 /** @param x Pixel relative to the region position.
2614  *  @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
2615  *  @return Snapped pixel relative to the region position.
2616  */
2617 double
2618 MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
2619 {
2620         return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
2621 }
2622
2623 double
2624 MidiRegionView::get_position_pixels()
2625 {
2626         framepos_t region_frame = get_position();
2627         return trackview.editor().sample_to_pixel(region_frame);
2628 }
2629
2630 double
2631 MidiRegionView::get_end_position_pixels()
2632 {
2633         framepos_t frame = get_position() + get_duration ();
2634         return trackview.editor().sample_to_pixel(frame);
2635 }
2636
2637 framepos_t
2638 MidiRegionView::source_beats_to_absolute_frames(Evoral::Beats beats) const
2639 {
2640         /* the time converter will return the frame corresponding to `beats'
2641            relative to the start of the source. The start of the source
2642            is an implied position given by region->position - region->start
2643         */
2644         const framepos_t source_start = _region->position() - _region->start();
2645         return  source_start +  _source_relative_time_converter.to (beats);
2646 }
2647
2648 Evoral::Beats
2649 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2650 {
2651         /* the `frames' argument needs to be converted into a frame count
2652            relative to the start of the source before being passed in to the
2653            converter.
2654         */
2655         const framepos_t source_start = _region->position() - _region->start();
2656         return  _source_relative_time_converter.from (frames - source_start);
2657 }
2658
2659 framepos_t
2660 MidiRegionView::region_beats_to_region_frames(Evoral::Beats beats) const
2661 {
2662         return _region_relative_time_converter.to(beats);
2663 }
2664
2665 Evoral::Beats
2666 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2667 {
2668         return _region_relative_time_converter.from(frames);
2669 }
2670
2671 double
2672 MidiRegionView::region_frames_to_region_beats_double (framepos_t frames) const
2673 {
2674         return _region_relative_time_converter_double.from(frames);
2675 }
2676
2677 void
2678 MidiRegionView::begin_resizing (bool /*at_front*/)
2679 {
2680         _resize_data.clear();
2681
2682         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2683                 Note *note = dynamic_cast<Note*> (*i);
2684
2685                 // only insert CanvasNotes into the map
2686                 if (note) {
2687                         NoteResizeData *resize_data = new NoteResizeData();
2688                         resize_data->note = note;
2689
2690                         // create a new SimpleRect from the note which will be the resize preview
2691                         ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2692                                                                                             ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2693
2694                         // calculate the colors: get the color settings
2695                         uint32_t fill_color = UINT_RGBA_CHANGE_A(
2696                                 UIConfiguration::instance().color ("midi note selected"),
2697                                 128);
2698
2699                         // make the resize preview notes more transparent and bright
2700                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2701
2702                         // calculate color based on note velocity
2703                         resize_rect->set_fill_color (UINT_INTERPOLATE(
2704                                 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2705                                 fill_color,
2706                                 0.85));
2707
2708                         resize_rect->set_outline_color (NoteBase::calculate_outline (
2709                                                                 UIConfiguration::instance().color ("midi note selected")));
2710
2711                         resize_data->resize_rect = resize_rect;
2712                         _resize_data.push_back(resize_data);
2713                 }
2714         }
2715 }
2716
2717 /** Update resizing notes while user drags.
2718  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2719  * @param at_front which end of the note (true == note on, false == note off)
2720  * @param delta_x change in mouse position since the start of the drag
2721  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2722  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2723  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2724  * as the \a primary note.
2725  * @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
2726  * @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
2727  */
2728 void
2729 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2730 {
2731         bool cursor_set = false;
2732         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
2733
2734         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2735                 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2736                 Note* canvas_note = (*i)->note;
2737                 double current_x;
2738
2739                 if (at_front) {
2740                         if (relative) {
2741                                 current_x = canvas_note->x0() + delta_x + snap_delta;
2742                         } else {
2743                                 current_x = primary->x0() + delta_x + snap_delta;
2744                         }
2745                 } else {
2746                         if (relative) {
2747                                 current_x = canvas_note->x1() + delta_x + snap_delta;
2748                         } else {
2749                                 current_x = primary->x1() + delta_x + snap_delta;
2750                         }
2751                 }
2752
2753                 if (current_x < 0) {
2754                         // This works even with snapping because RegionView::snap_frame_to_frame()
2755                         // snaps forward if the snapped sample is before the beginning of the region
2756                         current_x = 0;
2757                 }
2758                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2759                         current_x = trackview.editor().sample_to_pixel(_region->length());
2760                 }
2761
2762                 if (at_front) {
2763                         if (with_snap) {
2764                                 resize_rect->set_x0 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
2765                         } else {
2766                                 resize_rect->set_x0 (current_x - snap_delta);
2767                         }
2768                         resize_rect->set_x1 (canvas_note->x1());
2769                 } else {
2770                         if (with_snap) {
2771                                 resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
2772                         } else {
2773                                 resize_rect->set_x1 (current_x - snap_delta);
2774                         }
2775                         resize_rect->set_x0 (canvas_note->x0());
2776                 }
2777
2778                 if (!cursor_set) {
2779                         /* Convert snap delta from pixels to beats. */
2780                         framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
2781                         double snap_delta_beats = 0.0;
2782                         int sign = 1;
2783
2784                         /* negative beat offsets aren't allowed */
2785                         if (snap_delta_samps > 0) {
2786                                 snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
2787                         } else if (snap_delta_samps < 0) {
2788                                 snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
2789                                 sign = -1;
2790                         }
2791
2792                         const double  snapped_x = (with_snap ? snap_pixel_to_sample (current_x, ensure_snap) : trackview.editor ().pixel_to_sample (current_x));
2793                         Evoral::Beats beats     = region_frames_to_region_beats (snapped_x);
2794                         Evoral::Beats len       = Evoral::Beats();
2795
2796                         if (at_front) {
2797                                 if (beats < canvas_note->note()->end_time()) {
2798                                         len = canvas_note->note()->time() - beats + (sign * snap_delta_beats);
2799                                         len += canvas_note->note()->length();
2800                                 }
2801                         } else {
2802                                 if (beats >= canvas_note->note()->time()) {
2803                                         len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
2804                                 }
2805                         }
2806
2807                         len = std::max(Evoral::Beats(1 / 512.0), len);
2808
2809                         char buf[16];
2810                         snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2811                         show_verbose_cursor (buf, 0, 0);
2812
2813                         cursor_set = true;
2814                 }
2815
2816         }
2817 }
2818
2819
2820 /** Finish resizing notes when the user releases the mouse button.
2821  *  Parameters the same as for \a update_resizing().
2822  */
2823 void
2824 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2825 {
2826         _note_diff_command = _model->new_note_diff_command (_("resize notes"));
2827
2828         /* XX why doesn't snap_pixel_to_sample() handle this properly? */
2829         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
2830
2831         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2832                 Note*  canvas_note = (*i)->note;
2833                 ArdourCanvas::Rectangle*  resize_rect = (*i)->resize_rect;
2834
2835                 /* Get the new x position for this resize, which is in pixels relative
2836                  * to the region position.
2837                  */
2838
2839                 double current_x;
2840
2841                 if (at_front) {
2842                         if (relative) {
2843                                 current_x = canvas_note->x0() + delta_x + snap_delta;
2844                         } else {
2845                                 current_x = primary->x0() + delta_x + snap_delta;
2846                         }
2847                 } else {
2848                         if (relative) {
2849                                 current_x = canvas_note->x1() + delta_x + snap_delta;
2850                         } else {
2851                                 current_x = primary->x1() + delta_x + snap_delta;
2852                         }
2853                 }
2854
2855                 if (current_x < 0) {
2856                         current_x = 0;
2857                 }
2858                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2859                         current_x = trackview.editor().sample_to_pixel(_region->length());
2860                 }
2861
2862                 /* Convert snap delta from pixels to beats with sign. */
2863                 framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
2864                 double snap_delta_beats = 0.0;
2865                 int sign = 1;
2866
2867                 if (snap_delta_samps > 0) {
2868                         snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
2869                 } else if (snap_delta_samps < 0) {
2870                         snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
2871                         sign = -1;
2872                 }
2873
2874                 /* Convert the new x position to a frame within the source */
2875                 framepos_t current_fr;
2876                 if (with_snap) {
2877                         current_fr = snap_pixel_to_sample (current_x, ensure_snap) + _region->start ();
2878                 } else {
2879                         current_fr = trackview.editor().pixel_to_sample (current_x) + _region->start ();
2880                 }
2881
2882                 /* and then to beats */
2883                 const Evoral::Beats x_beats = region_frames_to_region_beats (current_fr);
2884
2885                 if (at_front && x_beats < canvas_note->note()->end_time()) {
2886                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
2887                         Evoral::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
2888                         len += canvas_note->note()->length();
2889
2890                         if (!!len) {
2891                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2892                         }
2893                 }
2894
2895                 if (!at_front) {
2896                         Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
2897                                                      x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
2898                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2899                 }
2900
2901                 delete resize_rect;
2902                 delete (*i);
2903         }
2904
2905         _resize_data.clear();
2906         apply_diff(true);
2907 }
2908
2909 void
2910 MidiRegionView::abort_resizing ()
2911 {
2912         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2913                 delete (*i)->resize_rect;
2914                 delete *i;
2915         }
2916
2917         _resize_data.clear ();
2918 }
2919
2920 void
2921 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2922 {
2923         uint8_t new_velocity;
2924
2925         if (relative) {
2926                 new_velocity = event->note()->velocity() + velocity;
2927                 clamp_to_0_127(new_velocity);
2928         } else {
2929                 new_velocity = velocity;
2930         }
2931
2932         event->set_selected (event->selected()); // change color
2933
2934         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2935 }
2936
2937 void
2938 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2939 {
2940         uint8_t new_note;
2941
2942         if (relative) {
2943                 new_note = event->note()->note() + note;
2944         } else {
2945                 new_note = note;
2946         }
2947
2948         clamp_to_0_127 (new_note);
2949         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2950 }
2951
2952 void
2953 MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::Beats end_delta)
2954 {
2955         bool change_start = false;
2956         bool change_length = false;
2957         Evoral::Beats new_start;
2958         Evoral::Beats new_length;
2959
2960         /* NOTE: the semantics of the two delta arguments are slightly subtle:
2961
2962            front_delta: if positive - move the start of the note later in time (shortening it)
2963            if negative - move the start of the note earlier in time (lengthening it)
2964
2965            end_delta:   if positive - move the end of the note later in time (lengthening it)
2966            if negative - move the end of the note earlier in time (shortening it)
2967         */
2968
2969         if (!!front_delta) {
2970                 if (front_delta < 0) {
2971
2972                         if (event->note()->time() < -front_delta) {
2973                                 new_start = Evoral::Beats();
2974                         } else {
2975                                 new_start = event->note()->time() + front_delta; // moves earlier
2976                         }
2977
2978                         /* start moved toward zero, so move the end point out to where it used to be.
2979                            Note that front_delta is negative, so this increases the length.
2980                         */
2981
2982                         new_length = event->note()->length() - front_delta;
2983                         change_start = true;
2984                         change_length = true;
2985
2986                 } else {
2987
2988                         Evoral::Beats new_pos = event->note()->time() + front_delta;
2989
2990                         if (new_pos < event->note()->end_time()) {
2991                                 new_start = event->note()->time() + front_delta;
2992                                 /* start moved toward the end, so move the end point back to where it used to be */
2993                                 new_length = event->note()->length() - front_delta;
2994                                 change_start = true;
2995                                 change_length = true;
2996                         }
2997                 }
2998
2999         }
3000
3001         if (!!end_delta) {
3002                 bool can_change = true;
3003                 if (end_delta < 0) {
3004                         if (event->note()->length() < -end_delta) {
3005                                 can_change = false;
3006                         }
3007                 }
3008
3009                 if (can_change) {
3010                         new_length = event->note()->length() + end_delta;
3011                         change_length = true;
3012                 }
3013         }
3014
3015         if (change_start) {
3016                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
3017         }
3018
3019         if (change_length) {
3020                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
3021         }
3022 }
3023
3024 void
3025 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
3026 {
3027         uint8_t new_channel;
3028
3029         if (relative) {
3030                 if (chn < 0.0) {
3031                         if (event->note()->channel() < -chn) {
3032                                 new_channel = 0;
3033                         } else {
3034                                 new_channel = event->note()->channel() + chn;
3035                         }
3036                 } else {
3037                         new_channel = event->note()->channel() + chn;
3038                 }
3039         } else {
3040                 new_channel = (uint8_t) chn;
3041         }
3042
3043         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
3044 }
3045
3046 void
3047 MidiRegionView::change_note_time (NoteBase* event, Evoral::Beats delta, bool relative)
3048 {
3049         Evoral::Beats new_time;
3050
3051         if (relative) {
3052                 if (delta < 0.0) {
3053                         if (event->note()->time() < -delta) {
3054                                 new_time = Evoral::Beats();
3055                         } else {
3056                                 new_time = event->note()->time() + delta;
3057                         }
3058                 } else {
3059                         new_time = event->note()->time() + delta;
3060                 }
3061         } else {
3062                 new_time = delta;
3063         }
3064
3065         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3066 }
3067
3068 void
3069 MidiRegionView::change_note_length (NoteBase* event, Evoral::Beats t)
3070 {
3071         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3072 }
3073
3074 void
3075 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3076 {
3077         int8_t delta;
3078         int8_t value = 0;
3079
3080         if (_selection.empty()) {
3081                 return;
3082         }
3083
3084         if (fine) {
3085                 delta = 1;
3086         } else {
3087                 delta = 10;
3088         }
3089
3090         if (!up) {
3091                 delta = -delta;
3092         }
3093
3094         if (!allow_smush) {
3095                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3096                         if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3097                                 goto cursor_label;
3098                         }
3099                 }
3100         }
3101
3102         start_note_diff_command (_("change velocities"));
3103
3104         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3105                 Selection::iterator next = i;
3106                 ++next;
3107
3108                 if (all_together) {
3109                         if (i == _selection.begin()) {
3110                                 change_note_velocity (*i, delta, true);
3111                                 value = (*i)->note()->velocity() + delta;
3112                         } else {
3113                                 change_note_velocity (*i, value, false);
3114                         }
3115
3116                 } else {
3117                         change_note_velocity (*i, delta, true);
3118                 }
3119
3120                 i = next;
3121         }
3122
3123         apply_diff();
3124
3125   cursor_label:
3126         if (!_selection.empty()) {
3127                 char buf[24];
3128                 snprintf (buf, sizeof (buf), "Vel %d",
3129                           (int) (*_selection.begin())->note()->velocity());
3130                 show_verbose_cursor (buf, 10, 10);
3131         }
3132 }
3133
3134
3135 void
3136 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3137 {
3138         if (_selection.empty()) {
3139                 return;
3140         }
3141
3142         int8_t delta;
3143
3144         if (fine) {
3145                 delta = 1;
3146         } else {
3147                 delta = 12;
3148         }
3149
3150         if (!up) {
3151                 delta = -delta;
3152         }
3153
3154         if (!allow_smush) {
3155                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3156                         if (!up) {
3157                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3158                                         return;
3159                                 }
3160                         } else {
3161                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
3162                                         return;
3163                                 }
3164                         }
3165                 }
3166         }
3167
3168         start_note_diff_command (_("transpose"));
3169
3170         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3171                 Selection::iterator next = i;
3172                 ++next;
3173                 change_note_note (*i, delta, true);
3174                 i = next;
3175         }
3176
3177         apply_diff ();
3178 }
3179
3180 void
3181 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::Beats delta, bool start, bool end)
3182 {
3183         if (!delta) {
3184                 if (fine) {
3185                         delta = Evoral::Beats(1.0/128.0);
3186                 } else {
3187                         /* grab the current grid distance */
3188                         delta = get_grid_beats(_region->position());
3189                 }
3190         }
3191
3192         if (shorter) {
3193                 delta = -delta;
3194         }
3195
3196         start_note_diff_command (_("change note lengths"));
3197
3198         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3199                 Selection::iterator next = i;
3200                 ++next;
3201
3202                 /* note the negation of the delta for start */
3203
3204                 trim_note (*i,
3205                            (start ? -delta : Evoral::Beats()),
3206                            (end   ? delta  : Evoral::Beats()));
3207                 i = next;
3208         }
3209
3210         apply_diff ();
3211
3212 }
3213
3214 void
3215 MidiRegionView::nudge_notes (bool forward, bool fine)
3216 {
3217         if (_selection.empty()) {
3218                 return;
3219         }
3220
3221         /* pick a note as the point along the timeline to get the nudge distance.
3222            its not necessarily the earliest note, so we may want to pull the notes out
3223            into a vector and sort before using the first one.
3224         */
3225
3226         const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3227         Evoral::Beats    delta;
3228
3229         if (!fine) {
3230
3231                 /* non-fine, move by 1 bar regardless of snap */
3232                 delta = Evoral::Beats(trackview.session()->tempo_map().meter_at_frame (ref_point).divisions_per_bar());
3233
3234         } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3235
3236                 /* grid is off - use nudge distance */
3237
3238                 framepos_t       unused;
3239                 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3240                 delta = region_frames_to_region_beats (fabs ((double)distance));
3241
3242         } else {
3243
3244                 /* use grid */
3245
3246                 framepos_t next_pos = ref_point;
3247
3248                 if (forward) {
3249                         if (max_framepos - 1 < next_pos) {
3250                                 next_pos += 1;
3251                         }
3252                 } else {
3253                         if (next_pos == 0) {
3254                                 return;
3255                         }
3256                         next_pos -= 1;
3257                 }
3258
3259                 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3260                 const framecnt_t distance = ref_point - next_pos;
3261                 delta = region_frames_to_region_beats (fabs ((double)distance));
3262         }
3263
3264         if (!delta) {
3265                 return;
3266         }
3267
3268         if (!forward) {
3269                 delta = -delta;
3270         }
3271
3272         start_note_diff_command (_("nudge"));
3273
3274         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3275                 Selection::iterator next = i;
3276                 ++next;
3277                 change_note_time (*i, delta, true);
3278                 i = next;
3279         }
3280
3281         apply_diff ();
3282 }
3283
3284 void
3285 MidiRegionView::change_channel(uint8_t channel)
3286 {
3287         start_note_diff_command(_("change channel"));
3288         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3289                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3290         }
3291
3292         apply_diff();
3293 }
3294
3295
3296 void
3297 MidiRegionView::note_entered(NoteBase* ev)
3298 {
3299         _note_entered = true;
3300
3301         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3302
3303         if (_mouse_state == SelectTouchDragging) {
3304
3305                 note_selected (ev, true);
3306
3307         } else if (editor->current_mouse_mode() == MouseContent) {
3308
3309                 remove_ghost_note ();
3310                 show_verbose_cursor (ev->note ());
3311
3312         } else if (editor->current_mouse_mode() == MouseDraw) {
3313
3314                 remove_ghost_note ();
3315                 show_verbose_cursor (ev->note ());
3316         }
3317 }
3318
3319 void
3320 MidiRegionView::note_left (NoteBase*)
3321 {
3322         _note_entered = false;
3323
3324         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3325                 (*i)->hide_velocity ();
3326         }
3327
3328         hide_verbose_cursor ();
3329 }
3330
3331 void
3332 MidiRegionView::patch_entered (PatchChange* p)
3333 {
3334         ostringstream s;
3335         /* XXX should get patch name if we can */
3336         s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3337           << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n'
3338           << _("Channel ") << ((int) p->patch()->channel() + 1);
3339         show_verbose_cursor (s.str(), 10, 20);
3340         p->item().grab_focus();
3341 }
3342
3343 void
3344 MidiRegionView::patch_left (PatchChange *)
3345 {
3346         hide_verbose_cursor ();
3347         /* focus will transfer back via the enter-notify event sent to this
3348          * midi region view.
3349          */
3350 }
3351
3352 void
3353 MidiRegionView::sysex_entered (SysEx* p)
3354 {
3355         ostringstream s;
3356         // CAIROCANVAS
3357         // need a way to extract text from p->_flag->_text
3358         // s << p->text();
3359         // show_verbose_cursor (s.str(), 10, 20);
3360         p->item().grab_focus();
3361 }
3362
3363 void
3364 MidiRegionView::sysex_left (SysEx *)
3365 {
3366         hide_verbose_cursor ();
3367         /* focus will transfer back via the enter-notify event sent to this
3368          * midi region view.
3369          */
3370 }
3371
3372 void
3373 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3374 {
3375         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3376         Editing::MouseMode mm = editor->current_mouse_mode();
3377         bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3378
3379         Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3380         if (can_set_cursor && ctx) {
3381                 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3382                         ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3383                 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3384                         ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3385                 } else {
3386                         ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3387                 }
3388         }
3389 }
3390
3391 uint32_t
3392 MidiRegionView::get_fill_color() const
3393 {
3394         const std::string mod_name = (_dragging ? "dragging region" :
3395                                       trackview.editor().internal_editing() ? "editable region" :
3396                                       "midi frame base");
3397         if (_selected) {
3398                 return UIConfiguration::instance().color_mod ("selected region base", mod_name);
3399         } else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) &&
3400                    !UIConfiguration::instance().get_color_regions_using_track_color()) {
3401                 return UIConfiguration::instance().color_mod ("midi frame base", mod_name);
3402         }
3403         return UIConfiguration::instance().color_mod (fill_color, mod_name);
3404 }
3405
3406 void
3407 MidiRegionView::midi_channel_mode_changed ()
3408 {
3409         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3410         uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3411         ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3412
3413         if (mode == ForceChannel) {
3414                 mask = 0xFFFF; // Show all notes as active (below)
3415         }
3416
3417         // Update notes for selection
3418         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3419                 (*i)->on_channel_selection_change (mask);
3420         }
3421
3422         _patch_changes.clear ();
3423         display_patch_changes ();
3424 }
3425
3426 void
3427 MidiRegionView::instrument_settings_changed ()
3428 {
3429         redisplay_model();
3430 }
3431
3432 void
3433 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3434 {
3435         if (_selection.empty()) {
3436                 return;
3437         }
3438
3439         PublicEditor& editor (trackview.editor());
3440
3441         switch (op) {
3442         case Delete:
3443                 /* XXX what to do ? */
3444                 break;
3445         case Cut:
3446         case Copy:
3447                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3448                 break;
3449         default:
3450                 break;
3451         }
3452
3453         if (op != Copy) {
3454
3455                 start_note_diff_command();
3456
3457                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3458                         switch (op) {
3459                         case Copy:
3460                                 break;
3461                         case Delete:
3462                         case Cut:
3463                         case Clear:
3464                                 note_diff_remove_note (*i);
3465                                 break;
3466                         }
3467                 }
3468
3469                 apply_diff();
3470         }
3471 }
3472
3473 MidiCutBuffer*
3474 MidiRegionView::selection_as_cut_buffer () const
3475 {
3476         Notes notes;
3477
3478         for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3479                 NoteType* n = (*i)->note().get();
3480                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3481         }
3482
3483         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3484         cb->set (notes);
3485
3486         return cb;
3487 }
3488
3489 /** This method handles undo */
3490 bool
3491 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3492 {
3493         bool commit = false;
3494         // Paste notes, if available
3495         MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3496         if (m != selection.midi_notes.end()) {
3497                 ctx.counts.increase_n_notes();
3498                 if (!(*m)->empty()) { commit = true; }
3499                 paste_internal(pos, ctx.count, ctx.times, **m);
3500         }
3501
3502         // Paste control points to automation children, if available
3503         typedef RouteTimeAxisView::AutomationTracks ATracks;
3504         const ATracks& atracks = midi_view()->automation_tracks();
3505         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3506                 if (a->second->paste(pos, selection, ctx)) {
3507                         commit = true;
3508                 }
3509         }
3510
3511         if (commit) {
3512                 trackview.editor().commit_reversible_command ();
3513         }
3514         return true;
3515 }
3516
3517 /** This method handles undo */
3518 void
3519 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3520 {
3521         if (mcb.empty()) {
3522                 return;
3523         }
3524
3525         start_note_diff_command (_("paste"));
3526
3527         const Evoral::Beats snap_beats    = get_grid_beats(pos);
3528         const Evoral::Beats first_time    = (*mcb.notes().begin())->time();
3529         const Evoral::Beats last_time     = (*mcb.notes().rbegin())->end_time();
3530         const Evoral::Beats duration      = last_time - first_time;
3531         const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
3532         const Evoral::Beats paste_offset  = snap_duration * paste_count;
3533         const Evoral::Beats pos_beats     = absolute_frames_to_source_beats(pos) + paste_offset;
3534         Evoral::Beats       end_point     = Evoral::Beats();
3535
3536         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3537                                                        first_time,
3538                                                        last_time,
3539                                                        duration, pos, _region->position(),
3540                                                        pos_beats));
3541
3542         clear_editor_note_selection ();
3543
3544         for (int n = 0; n < (int) times; ++n) {
3545
3546                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3547
3548                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3549                         copied_note->set_time (pos_beats + copied_note->time() - first_time);
3550                         copied_note->set_id (Evoral::next_event_id());
3551
3552                         /* make all newly added notes selected */
3553
3554                         note_diff_add_note (copied_note, true);
3555                         end_point = copied_note->end_time();
3556                 }
3557         }
3558
3559         /* if we pasted past the current end of the region, extend the region */
3560
3561         framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3562         framepos_t region_end = _region->position() + _region->length() - 1;
3563
3564         if (end_frame > region_end) {
3565
3566                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3567
3568                 _region->clear_changes ();
3569                 _region->set_length (end_frame - _region->position());
3570                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3571         }
3572
3573         apply_diff (true);
3574 }
3575
3576 struct EventNoteTimeEarlyFirstComparator {
3577         bool operator() (NoteBase* a, NoteBase* b) {
3578                 return a->note()->time() < b->note()->time();
3579         }
3580 };
3581
3582 void
3583 MidiRegionView::time_sort_events ()
3584 {
3585         if (!_sort_needed) {
3586                 return;
3587         }
3588
3589         EventNoteTimeEarlyFirstComparator cmp;
3590         _events.sort (cmp);
3591
3592         _sort_needed = false;
3593 }
3594
3595 void
3596 MidiRegionView::goto_next_note (bool add_to_selection)
3597 {
3598         bool use_next = false;
3599
3600         if (_events.back()->selected()) {
3601                 return;
3602         }
3603
3604         time_sort_events ();
3605
3606         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3607         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3608
3609         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3610                 if ((*i)->selected()) {
3611                         use_next = true;
3612                         continue;
3613                 } else if (use_next) {
3614                         if (channel_mask & (1 << (*i)->note()->channel())) {
3615                                 if (!add_to_selection) {
3616                                         unique_select (*i);
3617                                 } else {
3618                                         note_selected (*i, true, false);
3619                                 }
3620                                 return;
3621                         }
3622                 }
3623         }
3624
3625         /* use the first one */
3626
3627         if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3628                 unique_select (_events.front());
3629         }
3630 }
3631
3632 void
3633 MidiRegionView::goto_previous_note (bool add_to_selection)
3634 {
3635         bool use_next = false;
3636
3637         if (_events.front()->selected()) {
3638                 return;
3639         }
3640
3641         time_sort_events ();
3642
3643         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3644         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3645
3646         for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3647                 if ((*i)->selected()) {
3648                         use_next = true;
3649                         continue;
3650                 } else if (use_next) {
3651                         if (channel_mask & (1 << (*i)->note()->channel())) {
3652                                 if (!add_to_selection) {
3653                                         unique_select (*i);
3654                                 } else {
3655                                         note_selected (*i, true, false);
3656                                 }
3657                                 return;
3658                         }
3659                 }
3660         }
3661
3662         /* use the last one */
3663
3664         if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3665                 unique_select (*(_events.rbegin()));
3666         }
3667 }
3668
3669 void
3670 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3671 {
3672         bool had_selected = false;
3673
3674         time_sort_events ();
3675
3676         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3677                 if ((*i)->selected()) {
3678                         selected.insert ((*i)->note());
3679                         had_selected = true;
3680                 }
3681         }
3682
3683         if (allow_all_if_none_selected && !had_selected) {
3684                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3685                         selected.insert ((*i)->note());
3686                 }
3687         }
3688 }
3689
3690 void
3691 MidiRegionView::update_ghost_note (double x, double y)
3692 {
3693         x = std::max(0.0, x);
3694
3695         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3696
3697         _last_ghost_x = x;
3698         _last_ghost_y = y;
3699
3700         _note_group->canvas_to_item (x, y);
3701
3702         PublicEditor& editor = trackview.editor ();
3703
3704         framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3705         framecnt_t grid_frames;
3706         framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3707
3708         /* calculate time in beats relative to start of source */
3709         const Evoral::Beats length = get_grid_beats(unsnapped_frame);
3710         const Evoral::Beats time   = std::max(
3711                 Evoral::Beats(),
3712                 absolute_frames_to_source_beats (f + _region->position ()));
3713
3714         _ghost_note->note()->set_time (time);
3715         _ghost_note->note()->set_length (length);
3716         _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3717         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3718         _ghost_note->note()->set_velocity (get_velocity_for_add (time));
3719
3720         /* the ghost note does not appear in ghost regions, so pass false in here */
3721         update_note (_ghost_note, false);
3722
3723         show_verbose_cursor (_ghost_note->note ());
3724 }
3725
3726 void
3727 MidiRegionView::create_ghost_note (double x, double y)
3728 {
3729         remove_ghost_note ();
3730
3731         boost::shared_ptr<NoteType> g (new NoteType);
3732         if (midi_view()->note_mode() == Sustained) {
3733                 _ghost_note = new Note (*this, _note_group, g);
3734         } else {
3735                 _ghost_note = new Hit (*this, _note_group, 10, g);
3736         }
3737         _ghost_note->set_ignore_events (true);
3738         _ghost_note->set_outline_color (0x000000aa);
3739         update_ghost_note (x, y);
3740         _ghost_note->show ();
3741
3742         show_verbose_cursor (_ghost_note->note ());
3743 }
3744
3745 void
3746 MidiRegionView::remove_ghost_note ()
3747 {
3748         delete _ghost_note;
3749         _ghost_note = 0;
3750 }
3751
3752 void
3753 MidiRegionView::hide_verbose_cursor ()
3754 {
3755         trackview.editor().verbose_cursor()->hide ();
3756         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3757         if (mtv) {
3758                 mtv->set_note_highlight (NO_MIDI_NOTE);
3759         }
3760 }
3761
3762 void
3763 MidiRegionView::snap_changed ()
3764 {
3765         if (!_ghost_note) {
3766                 return;
3767         }
3768
3769         create_ghost_note (_last_ghost_x, _last_ghost_y);
3770 }
3771
3772 void
3773 MidiRegionView::drop_down_keys ()
3774 {
3775         _mouse_state = None;
3776 }
3777
3778 void
3779 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3780 {
3781         /* XXX: This is dead code.  What was it for? */
3782
3783         double note = midi_stream_view()->y_to_note(y);
3784         Events e;
3785         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3786
3787         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3788
3789         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3790                 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3791         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3792                 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3793         } else {
3794                 return;
3795         }
3796
3797         bool add_mrv_selection = false;
3798
3799         if (_selection.empty()) {
3800                 add_mrv_selection = true;
3801         }
3802
3803         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3804                 if (_selection.insert (*i).second) {
3805                         (*i)->set_selected (true);
3806                 }
3807         }
3808
3809         if (add_mrv_selection) {
3810                 PublicEditor& editor (trackview.editor());
3811                 editor.get_selection().add (this);
3812         }
3813 }
3814
3815 void
3816 MidiRegionView::color_handler ()
3817 {
3818         RegionView::color_handler ();
3819
3820         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3821                 (*i)->set_selected ((*i)->selected()); // will change color
3822         }
3823
3824         /* XXX probably more to do here */
3825 }
3826
3827 void
3828 MidiRegionView::enable_display (bool yn)
3829 {
3830         RegionView::enable_display (yn);
3831         if (yn) {
3832                 redisplay_model ();
3833         }
3834 }
3835
3836 void
3837 MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
3838 {
3839         if (_step_edit_cursor == 0) {
3840                 ArdourCanvas::Item* const group = get_canvas_group();
3841
3842                 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3843                 _step_edit_cursor->set_y0 (0);
3844                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3845                 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3846                 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3847         }
3848
3849         move_step_edit_cursor (pos);
3850         _step_edit_cursor->show ();
3851 }
3852
3853 void
3854 MidiRegionView::move_step_edit_cursor (Evoral::Beats pos)
3855 {
3856         _step_edit_cursor_position = pos;
3857
3858         if (_step_edit_cursor) {
3859                 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3860                 _step_edit_cursor->set_x0 (pixel);
3861                 set_step_edit_cursor_width (_step_edit_cursor_width);
3862         }
3863 }
3864
3865 void
3866 MidiRegionView::hide_step_edit_cursor ()
3867 {
3868         if (_step_edit_cursor) {
3869                 _step_edit_cursor->hide ();
3870         }
3871 }
3872
3873 void
3874 MidiRegionView::set_step_edit_cursor_width (Evoral::Beats beats)
3875 {
3876         _step_edit_cursor_width = beats;
3877
3878         if (_step_edit_cursor) {
3879                 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3880         }
3881 }
3882
3883 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
3884  *  @param w Source that the data will end up in.
3885  */
3886 void
3887 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3888 {
3889         if (!_active_notes) {
3890                 /* we aren't actively being recorded to */
3891                 return;
3892         }
3893
3894         boost::shared_ptr<MidiSource> src = w.lock ();
3895         if (!src || src != midi_region()->midi_source()) {
3896                 /* recorded data was not destined for our source */
3897                 return;
3898         }
3899
3900         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3901
3902         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3903
3904         framepos_t back = max_framepos;
3905
3906         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3907                 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3908
3909                 if (ev.is_channel_event()) {
3910                         if (get_channel_mode() == FilterChannels) {
3911                                 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3912                                         continue;
3913                                 }
3914                         }
3915                 }
3916
3917                 /* convert from session frames to source beats */
3918                 Evoral::Beats const time_beats = _source_relative_time_converter.from(
3919                         ev.time() - src->timeline_position() + _region->start());
3920
3921                 if (ev.type() == MIDI_CMD_NOTE_ON) {
3922                         boost::shared_ptr<NoteType> note (
3923                                 new NoteType (ev.channel(), time_beats, Evoral::Beats(), ev.note(), ev.velocity()));
3924
3925                         add_note (note, true);
3926
3927                         /* fix up our note range */
3928                         if (ev.note() < _current_range_min) {
3929                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3930                         } else if (ev.note() > _current_range_max) {
3931                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3932                         }
3933
3934                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3935                         resolve_note (ev.note (), time_beats);
3936                 }
3937
3938                 back = ev.time ();
3939         }
3940
3941         midi_stream_view()->check_record_layers (region(), back);
3942 }
3943
3944 void
3945 MidiRegionView::trim_front_starting ()
3946 {
3947         /* Reparent the note group to the region view's parent, so that it doesn't change
3948            when the region view is trimmed.
3949         */
3950         _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3951         _temporary_note_group->move (group->position ());
3952         _note_group->reparent (_temporary_note_group);
3953 }
3954
3955 void
3956 MidiRegionView::trim_front_ending ()
3957 {
3958         _note_group->reparent (group);
3959         delete _temporary_note_group;
3960         _temporary_note_group = 0;
3961
3962         if (_region->start() < 0) {
3963                 /* Trim drag made start time -ve; fix this */
3964                 midi_region()->fix_negative_start ();
3965         }
3966 }
3967
3968 void
3969 MidiRegionView::edit_patch_change (PatchChange* pc)
3970 {
3971         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3972
3973         int response = d.run();
3974
3975         switch (response) {
3976         case Gtk::RESPONSE_ACCEPT:
3977                 break;
3978         case Gtk::RESPONSE_REJECT:
3979                 delete_patch_change (pc);
3980                 return;
3981         default:
3982                 return;
3983         }
3984
3985         change_patch_change (pc->patch(), d.patch ());
3986 }
3987
3988 void
3989 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3990 {
3991         // CAIROCANVAS
3992         // sysyex object doesn't have a pointer to a sysex event
3993         // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3994         // c->remove (sysex->sysex());
3995         // _model->apply_command (*trackview.session(), c);
3996
3997         //_sys_exes.clear ();
3998         // display_sysexes();
3999 }
4000
4001 std::string
4002 MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
4003 {
4004         using namespace MIDI::Name;
4005         std::string name;
4006
4007         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4008         if (mtv) {
4009                 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
4010                 if (device_names) {
4011                         MIDI::Name::PatchPrimaryKey patch_key;
4012                         get_patch_key_at(n->time(), n->channel(), patch_key);
4013                         name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
4014                                                        n->channel(),
4015                                                        patch_key.bank(),
4016                                                        patch_key.program(),
4017                                                        note_value);
4018                 }
4019         }
4020
4021         char buf[128];
4022         snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
4023                   (int) note_value,
4024                   name.empty() ? Evoral::midi_note_name (note_value).c_str() : name.c_str(),
4025                   (int) n->channel() + 1,
4026                   (int) n->velocity());
4027
4028         return buf;
4029 }
4030
4031 void
4032 MidiRegionView::show_verbose_cursor_for_new_note_value(boost::shared_ptr<NoteType> current_note,
4033                                                        uint8_t new_value) const
4034 {
4035         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4036         if (mtv) {
4037                 mtv->set_note_highlight (new_value);
4038         }
4039
4040         show_verbose_cursor(get_note_name(current_note, new_value), 10, 20);
4041 }
4042
4043 void
4044 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
4045 {
4046         show_verbose_cursor_for_new_note_value(n, n->note());
4047 }
4048
4049 void
4050 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
4051 {
4052         trackview.editor().verbose_cursor()->set (text);
4053         trackview.editor().verbose_cursor()->show ();
4054         trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
4055 }
4056
4057 uint8_t
4058 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
4059 {
4060         if (_model->notes().empty()) {
4061                 return 0x40;  // No notes, use default
4062         }
4063
4064         MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
4065         if (m == _model->notes().begin()) {
4066                 // Before the start, use the velocity of the first note
4067                 return (*m)->velocity();
4068         } else if (m == _model->notes().end()) {
4069                 // Past the end, use the velocity of the last note
4070                 --m;
4071                 return (*m)->velocity();
4072         }
4073
4074         // Interpolate velocity of surrounding notes
4075         MidiModel::Notes::const_iterator n = m;
4076         --n;
4077
4078         const double frac = ((time - (*n)->time()).to_double() /
4079                              ((*m)->time() - (*n)->time()).to_double());
4080
4081         return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4082 }
4083
4084 /** @param p A session framepos.
4085  *  @param grid_frames Filled in with the number of frames that a grid interval is at p.
4086  *  @return p snapped to the grid subdivision underneath it.
4087  */
4088 framepos_t
4089 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
4090 {
4091         PublicEditor& editor = trackview.editor ();
4092         const Evoral::Beats p_beat = region_frames_to_region_beats (p);
4093         const Evoral::Beats grid_beats = get_grid_beats(p);
4094
4095         grid_frames = region_beats_to_region_frames (p_beat + grid_beats) - region_beats_to_region_frames (p_beat);
4096
4097         /* Hack so that we always snap to the note that we are over, instead of snapping
4098            to the next one if we're more than halfway through the one we're over.
4099         */
4100         if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
4101                 p -= grid_frames / 2;
4102         }
4103
4104         return snap_frame_to_frame (p);
4105 }
4106
4107 ChannelMode
4108 MidiRegionView::get_channel_mode () const
4109 {
4110         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4111         return rtav->midi_track()->get_playback_channel_mode();
4112 }
4113
4114 uint16_t
4115 MidiRegionView::get_selected_channels () const
4116 {
4117         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4118         return rtav->midi_track()->get_playback_channel_mask();
4119 }
4120
4121
4122 Evoral::Beats
4123 MidiRegionView::get_grid_beats(framepos_t pos) const
4124 {
4125         PublicEditor& editor  = trackview.editor();
4126         bool          success = false;
4127         Evoral::Beats beats   = editor.get_grid_type_as_beats (success, pos);
4128         if (!success) {
4129                 beats = Evoral::Beats(1);
4130         }
4131         return beats;
4132 }