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