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