include ArdourIcon in declared enums
[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                 framepos_t next_pos = ref_point;
3430
3431                 if (forward) {
3432                         if (max_framepos - 1 < next_pos) {
3433                                 next_pos += 1;
3434                         }
3435                 } else {
3436                         if (next_pos == 0) {
3437                                 return;
3438                         }
3439                         next_pos -= 1;
3440                 }
3441
3442                 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3443                 const framecnt_t distance = ref_point - next_pos;
3444                 delta = region_frames_to_region_beats (fabs ((double)distance));
3445         }
3446
3447         if (!delta) {
3448                 return;
3449         }
3450
3451         if (!forward) {
3452                 delta = -delta;
3453         }
3454
3455         start_note_diff_command (_("nudge"));
3456
3457         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3458                 Selection::iterator next = i;
3459                 ++next;
3460                 change_note_time (*i, delta, true);
3461                 i = next;
3462         }
3463
3464         apply_diff ();
3465 }
3466
3467 void
3468 MidiRegionView::change_channel(uint8_t channel)
3469 {
3470         start_note_diff_command(_("change channel"));
3471         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3472                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3473         }
3474
3475         apply_diff();
3476 }
3477
3478
3479 void
3480 MidiRegionView::note_entered(NoteBase* ev)
3481 {
3482         _entered_note = ev;
3483
3484         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3485
3486         if (_mouse_state == SelectTouchDragging) {
3487
3488                 note_selected (ev, true);
3489
3490         } else if (editor->current_mouse_mode() == MouseContent) {
3491
3492                 remove_ghost_note ();
3493                 show_verbose_cursor (ev->note ());
3494
3495         } else if (editor->current_mouse_mode() == MouseDraw) {
3496
3497                 remove_ghost_note ();
3498                 show_verbose_cursor (ev->note ());
3499         }
3500 }
3501
3502 void
3503 MidiRegionView::note_left (NoteBase*)
3504 {
3505         _entered_note = 0;
3506
3507         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3508                 (*i)->hide_velocity ();
3509         }
3510
3511         hide_verbose_cursor ();
3512 }
3513
3514 void
3515 MidiRegionView::patch_entered (PatchChange* p)
3516 {
3517         ostringstream s;
3518         s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3519           << instrument_info().get_patch_name_without (p->patch()->bank(), p->patch()->program(), p->patch()->channel()) << '\n'
3520           << _("Channel ") << ((int) p->patch()->channel() + 1);
3521         show_verbose_cursor (s.str(), 10, 20);
3522         p->item().grab_focus();
3523 }
3524
3525 void
3526 MidiRegionView::patch_left (PatchChange *)
3527 {
3528         hide_verbose_cursor ();
3529         /* focus will transfer back via the enter-notify event sent to this
3530          * midi region view.
3531          */
3532 }
3533
3534 void
3535 MidiRegionView::sysex_entered (SysEx* p)
3536 {
3537         // ostringstream s;
3538         // CAIROCANVAS
3539         // need a way to extract text from p->_flag->_text
3540         // s << p->text();
3541         // show_verbose_cursor (s.str(), 10, 20);
3542         p->item().grab_focus();
3543 }
3544
3545 void
3546 MidiRegionView::sysex_left (SysEx *)
3547 {
3548         hide_verbose_cursor ();
3549         /* focus will transfer back via the enter-notify event sent to this
3550          * midi region view.
3551          */
3552 }
3553
3554 void
3555 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3556 {
3557         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3558         Editing::MouseMode mm = editor->current_mouse_mode();
3559         bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3560
3561         Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3562         if (can_set_cursor && ctx) {
3563                 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3564                         ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3565                 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3566                         ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3567                 } else {
3568                         ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3569                 }
3570         }
3571 }
3572
3573 uint32_t
3574 MidiRegionView::get_fill_color() const
3575 {
3576         const std::string mod_name = (_dragging ? "dragging region" :
3577                                       trackview.editor().internal_editing() ? "editable region" :
3578                                       "midi frame base");
3579         if (_selected) {
3580                 return UIConfiguration::instance().color_mod ("selected region base", mod_name);
3581         } else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) &&
3582                    !UIConfiguration::instance().get_color_regions_using_track_color()) {
3583                 return UIConfiguration::instance().color_mod ("midi frame base", mod_name);
3584         }
3585         return UIConfiguration::instance().color_mod (fill_color, mod_name);
3586 }
3587
3588 void
3589 MidiRegionView::midi_channel_mode_changed ()
3590 {
3591         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3592         uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3593         ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3594
3595         if (mode == ForceChannel) {
3596                 mask = 0xFFFF; // Show all notes as active (below)
3597         }
3598
3599         // Update notes for selection
3600         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3601                 i->second->on_channel_selection_change (mask);
3602         }
3603
3604         _patch_changes.clear ();
3605         display_patch_changes ();
3606 }
3607
3608 void
3609 MidiRegionView::instrument_settings_changed ()
3610 {
3611         redisplay_model();
3612 }
3613
3614 void
3615 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3616 {
3617         if (_selection.empty()) {
3618                 return;
3619         }
3620
3621         PublicEditor& editor (trackview.editor());
3622
3623         switch (op) {
3624         case Delete:
3625                 /* XXX what to do ? */
3626                 break;
3627         case Cut:
3628         case Copy:
3629                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3630                 break;
3631         default:
3632                 break;
3633         }
3634
3635         if (op != Copy) {
3636
3637                 start_note_diff_command();
3638
3639                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3640                         switch (op) {
3641                         case Copy:
3642                                 break;
3643                         case Delete:
3644                         case Cut:
3645                         case Clear:
3646                                 note_diff_remove_note (*i);
3647                                 break;
3648                         }
3649                 }
3650
3651                 apply_diff();
3652         }
3653 }
3654
3655 MidiCutBuffer*
3656 MidiRegionView::selection_as_cut_buffer () const
3657 {
3658         Notes notes;
3659
3660         for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3661                 NoteType* n = (*i)->note().get();
3662                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3663         }
3664
3665         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3666         cb->set (notes);
3667
3668         return cb;
3669 }
3670
3671 /** This method handles undo */
3672 bool
3673 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
3674 {
3675         bool commit = false;
3676         // Paste notes, if available
3677         MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3678         if (m != selection.midi_notes.end()) {
3679                 ctx.counts.increase_n_notes();
3680                 if (!(*m)->empty()) {
3681                         commit = true;
3682                 }
3683                 paste_internal(pos, ctx.count, ctx.times, **m);
3684         }
3685
3686         // Paste control points to automation children, if available
3687         typedef RouteTimeAxisView::AutomationTracks ATracks;
3688         const ATracks& atracks = midi_view()->automation_tracks();
3689         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3690                 if (a->second->paste(pos, selection, ctx, sub_num)) {
3691                         if(!commit) {
3692                                 trackview.editor().begin_reversible_command (Operations::paste);
3693                         }
3694                         commit = true;
3695                 }
3696         }
3697
3698         if (commit) {
3699                 trackview.editor().commit_reversible_command ();
3700         }
3701         return true;
3702 }
3703
3704 /** This method handles undo */
3705 void
3706 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3707 {
3708         if (mcb.empty()) {
3709                 return;
3710         }
3711
3712         start_note_diff_command (_("paste"));
3713
3714         const Evoral::Beats snap_beats    = get_grid_beats(pos);
3715         const Evoral::Beats first_time    = (*mcb.notes().begin())->time();
3716         const Evoral::Beats last_time     = (*mcb.notes().rbegin())->end_time();
3717         const Evoral::Beats duration      = last_time - first_time;
3718         const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
3719         const Evoral::Beats paste_offset  = snap_duration * paste_count;
3720         const Evoral::Beats quarter_note     = absolute_frames_to_source_beats(pos) + paste_offset;
3721         Evoral::Beats       end_point     = Evoral::Beats();
3722
3723         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3724                                                        first_time,
3725                                                        last_time,
3726                                                        duration, pos, _region->position(),
3727                                                        quarter_note));
3728
3729         clear_editor_note_selection ();
3730
3731         for (int n = 0; n < (int) times; ++n) {
3732
3733                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3734
3735                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3736                         copied_note->set_time (quarter_note + copied_note->time() - first_time);
3737                         copied_note->set_id (Evoral::next_event_id());
3738
3739                         /* make all newly added notes selected */
3740
3741                         note_diff_add_note (copied_note, true);
3742                         end_point = copied_note->end_time();
3743                 }
3744         }
3745
3746         /* if we pasted past the current end of the region, extend the region */
3747
3748         framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3749         framepos_t region_end = _region->position() + _region->length() - 1;
3750
3751         if (end_frame > region_end) {
3752
3753                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3754
3755                 _region->clear_changes ();
3756                 /* we probably need to get the snap modifier somehow to make this correct for non-musical use */
3757                 _region->set_length (end_frame - _region->position(), trackview.editor().get_grid_music_divisions (0));
3758                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3759         }
3760
3761         apply_diff (true);
3762 }
3763
3764 struct EventNoteTimeEarlyFirstComparator {
3765         bool operator() (NoteBase* a, NoteBase* b) {
3766                 return a->note()->time() < b->note()->time();
3767         }
3768 };
3769
3770 void
3771 MidiRegionView::goto_next_note (bool add_to_selection)
3772 {
3773         bool use_next = false;
3774
3775         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3776         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3777         NoteBase* first_note = 0;
3778
3779         MidiModel::ReadLock lock(_model->read_lock());
3780         MidiModel::Notes& notes (_model->notes());
3781
3782         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
3783                 NoteBase* cne = 0;
3784                 if ((cne = find_canvas_note (*n))) {
3785
3786                         if (!first_note && (channel_mask & (1 << (*n)->channel()))) {
3787                                 first_note = cne;
3788                         }
3789
3790                         if (cne->selected()) {
3791                                 use_next = true;
3792                                 continue;
3793                         } else if (use_next) {
3794                                 if (channel_mask & (1 << (*n)->channel())) {
3795                                         if (!add_to_selection) {
3796                                                 unique_select (cne);
3797                                         } else {
3798                                                 note_selected (cne, true, false);
3799                                         }
3800
3801                                         return;
3802                                 }
3803                         }
3804                 }
3805         }
3806
3807         /* use the first one */
3808
3809         if (!_events.empty() && first_note) {
3810                 unique_select (first_note);
3811         }
3812 }
3813
3814 void
3815 MidiRegionView::goto_previous_note (bool add_to_selection)
3816 {
3817         bool use_next = false;
3818
3819         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3820         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3821         NoteBase* last_note = 0;
3822
3823         MidiModel::ReadLock lock(_model->read_lock());
3824         MidiModel::Notes& notes (_model->notes());
3825
3826         for (MidiModel::Notes::reverse_iterator n = notes.rbegin(); n != notes.rend(); ++n) {
3827                 NoteBase* cne = 0;
3828                 if ((cne = find_canvas_note (*n))) {
3829
3830                         if (!last_note && (channel_mask & (1 << (*n)->channel()))) {
3831                                 last_note = cne;
3832                         }
3833
3834                         if (cne->selected()) {
3835                                 use_next = true;
3836                                 continue;
3837
3838                         } else if (use_next) {
3839                                 if (channel_mask & (1 << (*n)->channel())) {
3840                                         if (!add_to_selection) {
3841                                                 unique_select (cne);
3842                                         } else {
3843                                                 note_selected (cne, true, false);
3844                                         }
3845
3846                                         return;
3847                                 }
3848                         }
3849                 }
3850         }
3851
3852         /* use the last one */
3853
3854         if (!_events.empty() && last_note) {
3855                 unique_select (last_note);
3856         }
3857 }
3858
3859 void
3860 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3861 {
3862         bool had_selected = false;
3863
3864         /* we previously time sorted events here, but Notes is a multiset sorted by time */
3865
3866         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3867                 if (i->second->selected()) {
3868                         selected.insert (i->first);
3869                         had_selected = true;
3870                 }
3871         }
3872
3873         if (allow_all_if_none_selected && !had_selected) {
3874                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3875                         selected.insert (i->first);
3876                 }
3877         }
3878 }
3879
3880 void
3881 MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
3882 {
3883         x = std::max(0.0, x);
3884
3885         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3886
3887         _last_ghost_x = x;
3888         _last_ghost_y = y;
3889
3890         _note_group->canvas_to_item (x, y);
3891
3892         PublicEditor& editor = trackview.editor ();
3893
3894         framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3895
3896         const int32_t divisions = editor.get_grid_music_divisions (state);
3897         const bool shift_snap = midi_view()->note_mode() != Percussive;
3898         const Evoral::Beats snapped_beats = snap_frame_to_grid_underneath (unsnapped_frame, divisions, shift_snap);
3899
3900         /* prevent Percussive mode from displaying a ghost hit at region end */
3901         if (!shift_snap && snapped_beats >= midi_region()->start_beats() + midi_region()->length_beats()) {
3902                 _ghost_note->hide();
3903                 hide_verbose_cursor ();
3904                 return;
3905         }
3906
3907         /* ghost note may have been snapped before region */
3908         if (_ghost_note && snapped_beats.to_double() < 0.0) {
3909                 _ghost_note->hide();
3910                 return;
3911
3912         } else if (_ghost_note) {
3913                 _ghost_note->show();
3914         }
3915
3916         /* calculate time in beats relative to start of source */
3917         const Evoral::Beats length = get_grid_beats(unsnapped_frame + _region->position());
3918
3919         _ghost_note->note()->set_time (snapped_beats);
3920         _ghost_note->note()->set_length (length);
3921         _ghost_note->note()->set_note (y_to_note (y));
3922         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3923         _ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats));
3924         /* the ghost note does not appear in ghost regions, so pass false in here */
3925         update_note (_ghost_note, false);
3926
3927         show_verbose_cursor (_ghost_note->note ());
3928 }
3929
3930 void
3931 MidiRegionView::create_ghost_note (double x, double y, uint32_t state)
3932 {
3933         remove_ghost_note ();
3934
3935         boost::shared_ptr<NoteType> g (new NoteType);
3936         if (midi_view()->note_mode() == Sustained) {
3937                 _ghost_note = new Note (*this, _note_group, g);
3938         } else {
3939                 _ghost_note = new Hit (*this, _note_group, 10, g);
3940         }
3941         _ghost_note->set_ignore_events (true);
3942         _ghost_note->set_outline_color (0x000000aa);
3943         update_ghost_note (x, y, state);
3944         _ghost_note->show ();
3945
3946         show_verbose_cursor (_ghost_note->note ());
3947 }
3948
3949 void
3950 MidiRegionView::remove_ghost_note ()
3951 {
3952         delete _ghost_note;
3953         _ghost_note = 0;
3954 }
3955
3956 void
3957 MidiRegionView::hide_verbose_cursor ()
3958 {
3959         trackview.editor().verbose_cursor()->hide ();
3960         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3961         if (mtv) {
3962                 mtv->set_note_highlight (NO_MIDI_NOTE);
3963         }
3964 }
3965
3966 void
3967 MidiRegionView::snap_changed ()
3968 {
3969         if (!_ghost_note) {
3970                 return;
3971         }
3972
3973         create_ghost_note (_last_ghost_x, _last_ghost_y, 0);
3974 }
3975
3976 void
3977 MidiRegionView::drop_down_keys ()
3978 {
3979         _mouse_state = None;
3980 }
3981
3982 void
3983 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3984 {
3985         /* XXX: This is dead code.  What was it for? */
3986
3987         double note = y_to_note(y);
3988         Events e;
3989         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3990
3991         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3992
3993         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3994                 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3995         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3996                 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3997         } else {
3998                 return;
3999         }
4000
4001         bool add_mrv_selection = false;
4002
4003         if (_selection.empty()) {
4004                 add_mrv_selection = true;
4005         }
4006
4007         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
4008                 if (_selection.insert (i->second).second) {
4009                         i->second->set_selected (true);
4010                 }
4011         }
4012
4013         if (add_mrv_selection) {
4014                 PublicEditor& editor (trackview.editor());
4015                 editor.get_selection().add (this);
4016         }
4017 }
4018
4019 void
4020 MidiRegionView::color_handler ()
4021 {
4022         RegionView::color_handler ();
4023
4024         _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
4025         _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
4026
4027         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
4028                 i->second->set_selected (i->second->selected()); // will change color
4029         }
4030
4031         /* XXX probably more to do here */
4032 }
4033
4034 void
4035 MidiRegionView::enable_display (bool yn)
4036 {
4037         RegionView::enable_display (yn);
4038 }
4039
4040 void
4041 MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
4042 {
4043         if (_step_edit_cursor == 0) {
4044                 ArdourCanvas::Item* const group = get_canvas_group();
4045
4046                 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
4047                 _step_edit_cursor->set_y0 (0);
4048                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
4049                 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
4050                 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
4051         }
4052
4053         move_step_edit_cursor (pos);
4054         _step_edit_cursor->show ();
4055 }
4056
4057 void
4058 MidiRegionView::move_step_edit_cursor (Evoral::Beats pos)
4059 {
4060         _step_edit_cursor_position = pos;
4061
4062         if (_step_edit_cursor) {
4063                 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
4064                 _step_edit_cursor->set_x0 (pixel);
4065                 set_step_edit_cursor_width (_step_edit_cursor_width);
4066         }
4067 }
4068
4069 void
4070 MidiRegionView::hide_step_edit_cursor ()
4071 {
4072         if (_step_edit_cursor) {
4073                 _step_edit_cursor->hide ();
4074         }
4075 }
4076
4077 void
4078 MidiRegionView::set_step_edit_cursor_width (Evoral::Beats beats)
4079 {
4080         _step_edit_cursor_width = beats;
4081
4082         if (_step_edit_cursor) {
4083                 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (
4084                                                    region_beats_to_region_frames (_step_edit_cursor_position + beats)
4085                                                    - region_beats_to_region_frames (_step_edit_cursor_position)));
4086         }
4087 }
4088
4089 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
4090  *  @param w Source that the data will end up in.
4091  */
4092 void
4093 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
4094 {
4095         if (!_active_notes) {
4096                 /* we aren't actively being recorded to */
4097                 return;
4098         }
4099
4100         boost::shared_ptr<MidiSource> src = w.lock ();
4101         if (!src || src != midi_region()->midi_source()) {
4102                 /* recorded data was not destined for our source */
4103                 return;
4104         }
4105
4106         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
4107
4108         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
4109
4110         framepos_t back = max_framepos;
4111
4112         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
4113                 const Evoral::Event<MidiBuffer::TimeType>& ev = *i;
4114
4115                 if (ev.is_channel_event()) {
4116                         if (get_channel_mode() == FilterChannels) {
4117                                 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
4118                                         continue;
4119                                 }
4120                         }
4121                 }
4122
4123                 /* convert from session frames to source beats */
4124                 Evoral::Beats const time_beats = _source_relative_time_converter.from(
4125                         ev.time() - src->timeline_position() + _region->start());
4126
4127                 if (ev.type() == MIDI_CMD_NOTE_ON) {
4128                         boost::shared_ptr<NoteType> note (
4129                                 new NoteType (ev.channel(), time_beats, Evoral::Beats(), ev.note(), ev.velocity()));
4130
4131                         add_note (note, true);
4132
4133                         /* fix up our note range */
4134                         if (ev.note() < _current_range_min) {
4135                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
4136                         } else if (ev.note() > _current_range_max) {
4137                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
4138                         }
4139
4140                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
4141                         resolve_note (ev.note (), time_beats);
4142                 }
4143
4144                 back = ev.time ();
4145         }
4146
4147         midi_stream_view()->check_record_layers (region(), back);
4148 }
4149
4150 void
4151 MidiRegionView::trim_front_starting ()
4152 {
4153         /* We used to eparent the note group to the region view's parent, so that it didn't change.
4154            now we update it.
4155         */
4156 }
4157
4158 void
4159 MidiRegionView::trim_front_ending ()
4160 {
4161         if (_region->start() < 0) {
4162                 /* Trim drag made start time -ve; fix this */
4163                 midi_region()->fix_negative_start ();
4164         }
4165 }
4166
4167 void
4168 MidiRegionView::edit_patch_change (PatchChange* pc)
4169 {
4170         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
4171
4172         int response = d.run();
4173
4174         switch (response) {
4175         case Gtk::RESPONSE_ACCEPT:
4176                 break;
4177         case Gtk::RESPONSE_REJECT:
4178                 delete_patch_change (pc);
4179                 return;
4180         default:
4181                 return;
4182         }
4183
4184         change_patch_change (pc->patch(), d.patch ());
4185 }
4186
4187 void
4188 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
4189 {
4190         // CAIROCANVAS
4191         // sysyex object doesn't have a pointer to a sysex event
4192         // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
4193         // c->remove (sysex->sysex());
4194         // _model->apply_command (*trackview.session(), c);
4195
4196         //_sys_exes.clear ();
4197         // display_sysexes();
4198 }
4199
4200 std::string
4201 MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
4202 {
4203         using namespace MIDI::Name;
4204         std::string name;
4205
4206         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4207         if (mtv) {
4208                 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
4209                 if (device_names) {
4210                         MIDI::Name::PatchPrimaryKey patch_key;
4211                         get_patch_key_at(n->time(), n->channel(), patch_key);
4212                         name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
4213                                                        n->channel(),
4214                                                        patch_key.bank(),
4215                                                        patch_key.program(),
4216                                                        note_value);
4217                 }
4218         }
4219
4220         char buf[128];
4221         snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
4222                   (int) note_value,
4223                   name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
4224                   (int) n->channel() + 1,
4225                   (int) n->velocity());
4226
4227         return buf;
4228 }
4229
4230 void
4231 MidiRegionView::show_verbose_cursor_for_new_note_value(boost::shared_ptr<NoteType> current_note,
4232                                                        uint8_t new_value) const
4233 {
4234         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4235         if (mtv) {
4236                 mtv->set_note_highlight (new_value);
4237         }
4238
4239         show_verbose_cursor(get_note_name(current_note, new_value), 10, 20);
4240 }
4241
4242 void
4243 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
4244 {
4245         show_verbose_cursor_for_new_note_value(n, n->note());
4246 }
4247
4248 void
4249 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
4250 {
4251         trackview.editor().verbose_cursor()->set (text);
4252         trackview.editor().verbose_cursor()->show ();
4253         trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
4254 }
4255
4256 uint8_t
4257 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
4258 {
4259         if (_model->notes().empty()) {
4260                 return 0x40;  // No notes, use default
4261         }
4262
4263         MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
4264         if (m == _model->notes().begin()) {
4265                 // Before the start, use the velocity of the first note
4266                 return (*m)->velocity();
4267         } else if (m == _model->notes().end()) {
4268                 // Past the end, use the velocity of the last note
4269                 --m;
4270                 return (*m)->velocity();
4271         }
4272
4273         // Interpolate velocity of surrounding notes
4274         MidiModel::Notes::const_iterator n = m;
4275         --n;
4276
4277         const double frac = ((time - (*n)->time()).to_double() /
4278                              ((*m)->time() - (*n)->time()).to_double());
4279
4280         return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4281 }
4282
4283 /** @param p A session framepos.
4284  *  @param divisions beat division to snap given by Editor::get_grid_music_divisions() where
4285  *  bar is -1, 0 is audio samples and a positive integer is beat subdivisions.
4286  *  @return beat duration of p snapped to the grid subdivision underneath it.
4287  */
4288 Evoral::Beats
4289 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions, bool shift_snap) const
4290 {
4291         TempoMap& map (trackview.session()->tempo_map());
4292         double eqaf = map.exact_qn_at_frame (p + _region->position(), divisions);
4293
4294         if (divisions != 0 && shift_snap) {
4295                 const double qaf = map.quarter_note_at_frame (p + _region->position());
4296                 /* Hack so that we always snap to the note that we are over, instead of snapping
4297                    to the next one if we're more than halfway through the one we're over.
4298                 */
4299                 const Evoral::Beats grid_beats = get_grid_beats (p + _region->position());
4300                 const double rem = eqaf - qaf;
4301                 if (rem >= 0.0) {
4302                         eqaf -= grid_beats.to_double();
4303                 }
4304         }
4305         const double session_start_off = _region->quarter_note() - midi_region()->start_beats();
4306
4307         return Evoral::Beats (eqaf - session_start_off);
4308 }
4309
4310 ChannelMode
4311 MidiRegionView::get_channel_mode () const
4312 {
4313         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4314         return rtav->midi_track()->get_playback_channel_mode();
4315 }
4316
4317 uint16_t
4318 MidiRegionView::get_selected_channels () const
4319 {
4320         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4321         return rtav->midi_track()->get_playback_channel_mask();
4322 }
4323
4324
4325 Evoral::Beats
4326 MidiRegionView::get_grid_beats(framepos_t pos) const
4327 {
4328         PublicEditor& editor  = trackview.editor();
4329         bool          success = false;
4330         Evoral::Beats beats   = editor.get_grid_type_as_beats (success, pos);
4331         if (!success) {
4332                 beats = Evoral::Beats(1);
4333         }
4334         return beats;
4335 }
4336 uint8_t
4337 MidiRegionView::y_to_note (double y) const
4338 {
4339         int const n = ((contents_height() - y) / contents_height() * (double)(_current_range_max - _current_range_min + 1))
4340                 + _current_range_min;
4341
4342         if (n < 0) {
4343                 return 0;
4344         } else if (n > 127) {
4345                 return 127;
4346         }
4347
4348         /* min due to rounding and/or off-by-one errors */
4349         return min ((uint8_t) n, _current_range_max);
4350 }
4351
4352 double
4353 MidiRegionView::note_to_y(uint8_t note) const
4354 {
4355         return contents_height() - (note + 1 - _current_range_min) * note_height() + 1;
4356 }