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