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