MidiRegionView::redisplay_model() - code cleanup
[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                                         for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1247                                                 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1248                                                 if (gr) {
1249                                                         gr->update_note (sus, !visible || gr->trackview.hidden());
1250                                                 }
1251                                         }
1252                                 } else if ((hit = dynamic_cast<Hit*>(cne))) {
1253
1254                                         if (visible) {
1255                                                 update_hit (hit);
1256                                         }
1257
1258                                         for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1259                                                 MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1260                                                 if (gr) {
1261                                                         gr->update_hit (hit, !visible || gr->trackview.hidden());
1262                                                 }
1263                                         }
1264                                 }
1265                                 ++i;
1266                         }
1267                 }
1268         }
1269
1270         for (MidiModel::Notes::iterator n = missing_notes.begin(); n != missing_notes.end(); ++n) {
1271                 boost::shared_ptr<NoteType> note (*n);
1272                 NoteBase* cne;
1273                 bool visible;
1274
1275                 if (note_in_region_range (note, visible)) {
1276                         if (visible) {
1277                                 cne = add_note (note, true);
1278                         } else {
1279                                 cne = add_note (note, false);
1280                         }
1281                 } else {
1282                         cne = add_note (note, false);
1283                 }
1284
1285                 for (set<Evoral::event_id_t>::iterator it = _pending_note_selection.begin(); it != _pending_note_selection.end(); ++it) {
1286                         if ((*it) == note->id()) {
1287                                 add_to_selection (cne);
1288                         }
1289                 }
1290         }
1291
1292         _sys_exes.clear();
1293
1294         display_sysexes();
1295         display_patch_changes ();
1296
1297         _marked_for_selection.clear ();
1298         _marked_for_velocity.clear ();
1299         _pending_note_selection.clear ();
1300
1301 }
1302
1303 void
1304 MidiRegionView::display_patch_changes ()
1305 {
1306         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1307         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
1308
1309         for (uint8_t i = 0; i < 16; ++i) {
1310                 display_patch_changes_on_channel (i, chn_mask & (1 << i));
1311         }
1312 }
1313
1314 /** @param active_channel true to display patch changes fully, false to display
1315  * them `greyed-out' (as on an inactive channel)
1316  */
1317 void
1318 MidiRegionView::display_patch_changes_on_channel (uint8_t channel, bool active_channel)
1319 {
1320         for (MidiModel::PatchChanges::const_iterator i = _model->patch_changes().begin(); i != _model->patch_changes().end(); ++i) {
1321                 boost::shared_ptr<PatchChange> p;
1322
1323                 if ((*i)->channel() != channel) {
1324                         continue;
1325                 }
1326
1327                 if ((p = find_canvas_patch_change (*i)) != 0) {
1328
1329                         const framecnt_t region_frames = source_beats_to_region_frames ((*i)->time());
1330
1331                         if (region_frames < 0 || region_frames >= _region->length()) {
1332                                 p->hide();
1333                         } else {
1334                                 const double x = trackview.editor().sample_to_pixel (region_frames);
1335                                 const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1336                                 p->canvas_item()->set_position (ArdourCanvas::Duple (x, 1.0));
1337                                 p->flag()->set_text (patch_name);
1338
1339                                 p->show();
1340                         }
1341
1342                 } else {
1343                         const string patch_name = instrument_info().get_patch_name ((*i)->bank(), (*i)->program(), channel);
1344                         add_canvas_patch_change (*i, patch_name, active_channel);
1345                 }
1346         }
1347 }
1348
1349 void
1350 MidiRegionView::display_sysexes()
1351 {
1352         bool have_periodic_system_messages = false;
1353         bool display_periodic_messages = true;
1354
1355         if (!UIConfiguration::instance().get_never_display_periodic_midi()) {
1356
1357                 for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1358                         if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
1359                                 have_periodic_system_messages = true;
1360                                 break;
1361                         }
1362                 }
1363
1364                 if (have_periodic_system_messages) {
1365                         double zoom = trackview.editor().get_current_zoom (); // frames per pixel
1366
1367                         /* get an approximate value for the number of samples per video frame */
1368
1369                         double video_frame = trackview.session()->frame_rate() * (1.0/30);
1370
1371                         /* if we are zoomed out beyond than the cutoff (i.e. more
1372                          * frames per pixel than frames per 4 video frames), don't
1373                          * show periodic sysex messages.
1374                          */
1375
1376                         if (zoom > (video_frame*4)) {
1377                                 display_periodic_messages = false;
1378                         }
1379                 }
1380         } else {
1381                 display_periodic_messages = false;
1382         }
1383
1384         for (MidiModel::SysExes::const_iterator i = _model->sysexes().begin(); i != _model->sysexes().end(); ++i) {
1385                 Evoral::Beats time = (*i)->time();
1386
1387                 if ((*i)->is_spp() || (*i)->is_mtc_quarter() || (*i)->is_mtc_full()) {
1388                         if (!display_periodic_messages) {
1389                                 continue;
1390                         }
1391                 }
1392
1393                 ostringstream str;
1394                 str << hex;
1395                 for (uint32_t b = 0; b < (*i)->size(); ++b) {
1396                         str << int((*i)->buffer()[b]);
1397                         if (b != (*i)->size() -1) {
1398                                 str << " ";
1399                         }
1400                 }
1401                 string text = str.str();
1402
1403                 const double x = trackview.editor().sample_to_pixel(source_beats_to_region_frames(time));
1404
1405                 double height = midi_stream_view()->contents_height();
1406
1407                 // CAIROCANVAS: no longer passing *i (the sysex event) to the
1408                 // SysEx canvas object!!!
1409
1410                 boost::shared_ptr<SysEx> sysex = boost::shared_ptr<SysEx>(
1411                         new SysEx (*this, _note_group, text, height, x, 1.0));
1412
1413                 // Show unless message is beyond the region bounds
1414                 if (time - _region->start() >= _region->length() || time < _region->start()) {
1415                         sysex->hide();
1416                 } else {
1417                         sysex->show();
1418                 }
1419
1420                 _sys_exes.push_back(sysex);
1421         }
1422 }
1423
1424 MidiRegionView::~MidiRegionView ()
1425 {
1426         in_destructor = true;
1427
1428         hide_verbose_cursor ();
1429
1430         delete _list_editor;
1431
1432         RegionViewGoingAway (this); /* EMIT_SIGNAL */
1433
1434         if (_active_notes) {
1435                 end_write();
1436         }
1437         _entered_note = 0;
1438         clear_events ();
1439
1440         delete _note_group;
1441         delete _note_diff_command;
1442         delete _step_edit_cursor;
1443 }
1444
1445 void
1446 MidiRegionView::region_resized (const PropertyChange& what_changed)
1447 {
1448         RegionView::region_resized(what_changed); // calls RegionView::set_duration()
1449
1450         if (what_changed.contains (ARDOUR::Properties::position)) {
1451                 _region_relative_time_converter.set_origin_b(_region->position());
1452                 _region_relative_time_converter_double.set_origin_b(_region->position());
1453                 /* reset_width dependent_items() redisplays model */
1454
1455         }
1456
1457         if (what_changed.contains (ARDOUR::Properties::start) ||
1458             what_changed.contains (ARDOUR::Properties::position)) {
1459                 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1460         }
1461         /* catch end and start trim so we can update the view*/
1462         if (!what_changed.contains (ARDOUR::Properties::start) &&
1463             what_changed.contains (ARDOUR::Properties::length)) {
1464                 enable_display (true);
1465         } else if (what_changed.contains (ARDOUR::Properties::start) &&
1466             what_changed.contains (ARDOUR::Properties::length)) {
1467                 enable_display (true);
1468         }
1469 }
1470
1471 void
1472 MidiRegionView::reset_width_dependent_items (double pixel_width)
1473 {
1474         RegionView::reset_width_dependent_items(pixel_width);
1475
1476         if (_enable_display) {
1477                 redisplay_model();
1478         }
1479
1480         bool hide_all = false;
1481         PatchChanges::iterator x = _patch_changes.begin();
1482         if (x != _patch_changes.end()) {
1483                 hide_all = x->second->flag()->width() >= _pixel_width;
1484         }
1485
1486         if (hide_all) {
1487                 for (; x != _patch_changes.end(); ++x) {
1488                         x->second->hide();
1489                 }
1490         }
1491
1492         move_step_edit_cursor (_step_edit_cursor_position);
1493         set_step_edit_cursor_width (_step_edit_cursor_width);
1494 }
1495
1496 void
1497 MidiRegionView::set_height (double height)
1498 {
1499         double old_height = _height;
1500         RegionView::set_height(height);
1501
1502         apply_note_range (midi_stream_view()->lowest_note(),
1503                           midi_stream_view()->highest_note(),
1504                           height != old_height);
1505
1506         if (name_text) {
1507                 name_text->raise_to_top();
1508         }
1509
1510         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1511                 (*x).second->set_height (midi_stream_view()->contents_height());
1512         }
1513
1514         if (_step_edit_cursor) {
1515                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1516         }
1517 }
1518
1519
1520 /** Apply the current note range from the stream view
1521  * by repositioning/hiding notes as necessary
1522  */
1523 void
1524 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1525 {
1526         if (!_enable_display) {
1527                 return;
1528         }
1529
1530         if (!force && _current_range_min == min && _current_range_max == max) {
1531                 return;
1532         }
1533
1534         _current_range_min = min;
1535         _current_range_max = max;
1536
1537         redisplay_model ();
1538 }
1539
1540 GhostRegion*
1541 MidiRegionView::add_ghost (TimeAxisView& tv)
1542 {
1543         double unit_position = _region->position () / samples_per_pixel;
1544         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1545         MidiGhostRegion* ghost;
1546
1547         if (mtv && mtv->midi_view()) {
1548                 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1549                    to allow having midi notes on top of note lines and waveforms.
1550                 */
1551                 ghost = new MidiGhostRegion (*this, *mtv->midi_view(), trackview, unit_position);
1552         } else {
1553                 ghost = new MidiGhostRegion (*this, tv, trackview, unit_position);
1554         }
1555
1556         ghost->set_colors ();
1557         ghost->set_height ();
1558         ghost->set_duration (_region->length() / samples_per_pixel);
1559
1560         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1561                 ghost->add_note(i->second);
1562         }
1563
1564         ghosts.push_back (ghost);
1565         enable_display (true);
1566         return ghost;
1567 }
1568
1569
1570 /** Begin tracking note state for successive calls to add_event
1571  */
1572 void
1573 MidiRegionView::begin_write()
1574 {
1575         if (_active_notes) {
1576                 delete[] _active_notes;
1577         }
1578         _active_notes = new Note*[128];
1579         for (unsigned i = 0; i < 128; ++i) {
1580                 _active_notes[i] = 0;
1581         }
1582 }
1583
1584
1585 /** Destroy note state for add_event
1586  */
1587 void
1588 MidiRegionView::end_write()
1589 {
1590         delete[] _active_notes;
1591         _active_notes = 0;
1592         _marked_for_selection.clear();
1593         _marked_for_velocity.clear();
1594 }
1595
1596
1597 /** Resolve an active MIDI note (while recording).
1598  */
1599 void
1600 MidiRegionView::resolve_note(uint8_t note, Evoral::Beats end_time)
1601 {
1602         if (midi_view()->note_mode() != Sustained) {
1603                 return;
1604         }
1605
1606         if (_active_notes && _active_notes[note]) {
1607                 /* Set note length so update_note() works.  Note this is a local note
1608                    for recording, not from a model, so we can safely mess with it. */
1609                 _active_notes[note]->note()->set_length(
1610                         end_time - _active_notes[note]->note()->time());
1611
1612                 /* End time is relative to the region being recorded. */
1613                 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1614
1615                 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1616                 _active_notes[note]->set_outline_all ();
1617                 _active_notes[note] = 0;
1618         }
1619 }
1620
1621
1622 /** Extend active notes to rightmost edge of region (if length is changed)
1623  */
1624 void
1625 MidiRegionView::extend_active_notes()
1626 {
1627         if (!_active_notes) {
1628                 return;
1629         }
1630
1631         for (unsigned i = 0; i < 128; ++i) {
1632                 if (_active_notes[i]) {
1633                         _active_notes[i]->set_x1(
1634                                 trackview.editor().sample_to_pixel(_region->length()));
1635                 }
1636         }
1637 }
1638
1639 void
1640 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1641 {
1642         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1643                 return;
1644         }
1645
1646         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1647
1648         if (!route_ui || !route_ui->midi_track()) {
1649                 return;
1650         }
1651
1652         NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1653         np->add (note);
1654         np->play ();
1655
1656         /* NotePlayer deletes itself */
1657 }
1658
1659 void
1660 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1661 {
1662         const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1663         start_playing_midi_chord(notes);
1664 }
1665
1666 void
1667 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1668 {
1669         if (_no_sound_notes || !UIConfiguration::instance().get_sound_midi_notes()) {
1670                 return;
1671         }
1672
1673         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1674
1675         if (!route_ui || !route_ui->midi_track()) {
1676                 return;
1677         }
1678
1679         NotePlayer* player = new NotePlayer (route_ui->midi_track());
1680
1681         for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1682                 player->add (*n);
1683         }
1684
1685         player->play ();
1686 }
1687
1688
1689 bool
1690 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1691 {
1692         const boost::shared_ptr<ARDOUR::MidiRegion> midi_reg = midi_region();
1693
1694         /* must compare double explicitly as Beats::operator< rounds to ppqn */
1695         const bool outside = (note->time().to_double() < midi_reg->start_beats() ||
1696                               note->time().to_double() >= midi_reg->start_beats() + midi_reg->length_beats());
1697
1698         visible = (note->note() >= _current_range_min) &&
1699                 (note->note() <= _current_range_max);
1700
1701         return !outside;
1702 }
1703
1704 void
1705 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1706 {
1707         Note* sus = NULL;
1708         Hit*  hit = NULL;
1709         if ((sus = dynamic_cast<Note*>(note))) {
1710                 update_sustained(sus, update_ghost_regions);
1711         } else if ((hit = dynamic_cast<Hit*>(note))) {
1712                 update_hit(hit, update_ghost_regions);
1713         }
1714 }
1715
1716 /** Update a canvas note's size from its model note.
1717  *  @param ev Canvas note to update.
1718  *  @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1719  */
1720 void
1721 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1722 {
1723         TempoMap& map (trackview.session()->tempo_map());
1724         const boost::shared_ptr<ARDOUR::MidiRegion> mr = midi_region();
1725         boost::shared_ptr<NoteType> note = ev->note();
1726
1727         const double session_source_start = _region->quarter_note() - mr->start_beats();
1728         const framepos_t note_start_frames = map.frame_at_quarter_note (note->time().to_double() + session_source_start) - _region->position();
1729
1730         const double x0 = trackview.editor().sample_to_pixel (note_start_frames);
1731         double x1;
1732         const double y0 = 1 + floor(note_to_y(note->note()));
1733         double y1;
1734
1735         /* trim note display to not overlap the end of its region */
1736         if (note->length().to_double() > 0.0) {
1737                 double note_end_time = note->end_time().to_double();
1738
1739                 if (note_end_time > mr->start_beats() + mr->length_beats()) {
1740                         note_end_time = mr->start_beats() + mr->length_beats();
1741                 }
1742
1743                 const framepos_t note_end_frames = map.frame_at_quarter_note (session_source_start + note_end_time) - _region->position();
1744
1745                 x1 = std::max(1., trackview.editor().sample_to_pixel (note_end_frames)) - 1;
1746         } else {
1747                 x1 = std::max(1., trackview.editor().sample_to_pixel (_region->length())) - 1;
1748         }
1749
1750         y1 = y0 + std::max(1., floor(note_height()) - 1);
1751
1752         ev->set (ArdourCanvas::Rect (x0, y0, x1, y1));
1753
1754         if (!note->length()) {
1755                 if (_active_notes && note->note() < 128) {
1756                         Note* const old_rect = _active_notes[note->note()];
1757                         if (old_rect) {
1758                                 /* There is an active note on this key, so we have a stuck
1759                                    note.  Finish the old rectangle here. */
1760                                 old_rect->set_x1 (x1);
1761                                 old_rect->set_outline_all ();
1762                         }
1763                         _active_notes[note->note()] = ev;
1764                 }
1765                 /* outline all but right edge */
1766                 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1767                                               ArdourCanvas::Rectangle::TOP|
1768                                               ArdourCanvas::Rectangle::LEFT|
1769                                               ArdourCanvas::Rectangle::BOTTOM));
1770         } else {
1771                 /* outline all edges */
1772                 ev->set_outline_all ();
1773         }
1774
1775         // Update color in case velocity has changed
1776         const uint32_t base_col = ev->base_color();
1777         ev->set_fill_color(base_col);
1778         ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
1779
1780 }
1781
1782 void
1783 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1784 {
1785         boost::shared_ptr<NoteType> note = ev->note();
1786
1787         const double note_time_qn = note->time().to_double() + (_region->quarter_note() - midi_region()->start_beats());
1788         const framepos_t note_start_frames = trackview.session()->tempo_map().frame_at_quarter_note (note_time_qn) - _region->position();
1789
1790         const double x = trackview.editor().sample_to_pixel(note_start_frames);
1791         const double diamond_size = std::max(1., floor(note_height()) - 2.);
1792         const double y = 1.5 + floor(note_to_y(note->note())) + diamond_size * .5;
1793
1794         // see DnD note in MidiRegionView::apply_note_range() above
1795         if (y <= 0 || y >= _height) {
1796                 ev->hide();
1797         } else {
1798                 ev->show();
1799         }
1800
1801         ev->set_position (ArdourCanvas::Duple (x, y));
1802         ev->set_height (diamond_size);
1803
1804         // Update color in case velocity has changed
1805         const uint32_t base_col = ev->base_color();
1806         ev->set_fill_color(base_col);
1807         ev->set_outline_color(ev->calculate_outline(base_col, ev->selected()));
1808
1809 }
1810
1811 /** Add a MIDI note to the view (with length).
1812  *
1813  * If in sustained mode, notes with length 0 will be considered active
1814  * notes, and resolve_note should be called when the corresponding note off
1815  * event arrives, to properly display the note.
1816  */
1817 NoteBase*
1818 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1819 {
1820         NoteBase* event = 0;
1821
1822         if (midi_view()->note_mode() == Sustained) {
1823
1824                 Note* ev_rect = new Note (*this, _note_group, note); // XXX may leak
1825
1826                 update_sustained (ev_rect);
1827
1828                 event = ev_rect;
1829
1830         } else if (midi_view()->note_mode() == Percussive) {
1831
1832                 const double diamond_size = std::max(1., floor(note_height()) - 2.);
1833
1834                 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note); // XXX may leak
1835
1836                 update_hit (ev_diamond);
1837
1838                 event = ev_diamond;
1839
1840         } else {
1841                 event = 0;
1842         }
1843
1844         if (event) {
1845                 MidiGhostRegion* gr;
1846
1847                 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1848                         if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1849                                 gr->add_note(event);
1850                         }
1851                 }
1852
1853                 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1854                         note_selected(event, true);
1855                 }
1856
1857                 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1858                         event->show_velocity();
1859                 }
1860
1861                 event->on_channel_selection_change (get_selected_channels());
1862                 _events.insert (make_pair (event->note(), event));
1863
1864                 if (visible) {
1865                         event->show();
1866                 } else {
1867                         event->hide ();
1868                 }
1869         }
1870
1871         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1872         MidiStreamView* const view = mtv->midi_view();
1873
1874         view->update_note_range (note->note());
1875         return event;
1876 }
1877
1878 void
1879 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1880                                Evoral::Beats pos, Evoral::Beats len)
1881 {
1882         boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1883
1884         /* potentially extend region to hold new note */
1885
1886         framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1887         framepos_t region_end = _region->last_frame();
1888
1889         if (end_frame > region_end) {
1890                 /* XX sets length in beats from audio space. make musical */
1891                 _region->set_length (end_frame - _region->position(), 0);
1892         }
1893
1894         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1895         MidiStreamView* const view = mtv->midi_view();
1896
1897         view->update_note_range(new_note->note());
1898
1899         _marked_for_selection.clear ();
1900
1901         start_note_diff_command (_("step add"));
1902
1903         clear_editor_note_selection ();
1904         note_diff_add_note (new_note, true, false);
1905
1906         apply_diff();
1907
1908         // last_step_edit_note = new_note;
1909 }
1910
1911 void
1912 MidiRegionView::step_sustain (Evoral::Beats beats)
1913 {
1914         change_note_lengths (false, false, beats, false, true);
1915 }
1916
1917 /** Add a new patch change flag to the canvas.
1918  * @param patch the patch change to add
1919  * @param the text to display in the flag
1920  * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1921  */
1922 void
1923 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1924 {
1925         framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1926         const double x = trackview.editor().sample_to_pixel (region_frames);
1927
1928         double const height = midi_stream_view()->contents_height();
1929
1930         // CAIROCANVAS: active_channel info removed from PatcChange constructor
1931         // so we need to do something more sophisticated to keep its color
1932         // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1933         // up to date.
1934         boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1935                 new PatchChange(*this, group,
1936                                 displaytext,
1937                                 height,
1938                                 x, 1.0,
1939                                 instrument_info(),
1940                                 patch,
1941                                 _patch_change_outline,
1942                                 _patch_change_fill)
1943                 );
1944
1945         if (patch_change->item().width() < _pixel_width) {
1946                 // Show unless patch change is beyond the region bounds
1947                 if (region_frames < 0 || region_frames >= _region->length()) {
1948                         patch_change->hide();
1949                 } else {
1950                         patch_change->show();
1951                 }
1952         } else {
1953                 patch_change->hide ();
1954         }
1955
1956         _patch_changes.insert (make_pair (patch, patch_change));
1957 }
1958
1959 void
1960 MidiRegionView::remove_canvas_patch_change (PatchChange* pc)
1961 {
1962         /* remove the canvas item */
1963         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1964                 if (x->second->patch() == pc->patch()) {
1965                         _patch_changes.erase (x);
1966                         break;
1967                 }
1968         }
1969 }
1970
1971 MIDI::Name::PatchPrimaryKey
1972 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1973 {
1974         return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1975 }
1976
1977 /// Return true iff @p pc applies to the given time on the given channel.
1978 static bool
1979 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::Beats time, uint8_t channel)
1980 {
1981         return pc->time() <= time && pc->channel() == channel;
1982 }
1983
1984 void
1985 MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1986 {
1987         // The earliest event not before time
1988         MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1989
1990         // Go backwards until we find the latest PC for this channel, or the start
1991         while (i != _model->patch_changes().begin() &&
1992                (i == _model->patch_changes().end() ||
1993                 !patch_applies(*i, time, channel))) {
1994                 --i;
1995         }
1996
1997         if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1998                 key.set_bank((*i)->bank());
1999                 key.set_program((*i)->program ());
2000         } else {
2001                 key.set_bank(0);
2002                 key.set_program(0);
2003         }
2004 }
2005
2006 void
2007 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
2008 {
2009         string name = _("alter patch change");
2010         trackview.editor().begin_reversible_command (name);
2011
2012         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2013
2014         if (pc.patch()->program() != new_patch.program()) {
2015                 c->change_program (pc.patch (), new_patch.program());
2016         }
2017
2018         int const new_bank = new_patch.bank();
2019         if (pc.patch()->bank() != new_bank) {
2020                 c->change_bank (pc.patch (), new_bank);
2021         }
2022
2023         _model->apply_command (*trackview.session(), c);
2024         trackview.editor().commit_reversible_command ();
2025
2026         remove_canvas_patch_change (&pc);
2027         display_patch_changes ();
2028 }
2029
2030 void
2031 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change)
2032 {
2033         string name = _("alter patch change");
2034         trackview.editor().begin_reversible_command (name);
2035         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2036
2037         if (old_change->time() != new_change.time()) {
2038                 c->change_time (old_change, new_change.time());
2039         }
2040
2041         if (old_change->channel() != new_change.channel()) {
2042                 c->change_channel (old_change, new_change.channel());
2043         }
2044
2045         if (old_change->program() != new_change.program()) {
2046                 c->change_program (old_change, new_change.program());
2047         }
2048
2049         if (old_change->bank() != new_change.bank()) {
2050                 c->change_bank (old_change, new_change.bank());
2051         }
2052
2053         _model->apply_command (*trackview.session(), c);
2054         trackview.editor().commit_reversible_command ();
2055
2056         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
2057                 if (x->second->patch() == old_change) {
2058                         _patch_changes.erase (x);
2059                         break;
2060                 }
2061         }
2062
2063         display_patch_changes ();
2064 }
2065
2066 /** Add a patch change to the region.
2067  *  @param t Time in frames relative to region position
2068  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2069  *  MidiTimeAxisView::get_channel_for_add())
2070  */
2071 void
2072 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
2073 {
2074         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2075         string name = _("add patch change");
2076
2077         trackview.editor().begin_reversible_command (name);
2078         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2079         c->add (MidiModel::PatchChangePtr (
2080                         new Evoral::PatchChange<Evoral::Beats> (
2081                                 absolute_frames_to_source_beats (_region->position() + t),
2082                                 mtv->get_channel_for_add(), patch.program(), patch.bank()
2083                                 )
2084                         )
2085                 );
2086
2087         _model->apply_command (*trackview.session(), c);
2088         trackview.editor().commit_reversible_command ();
2089
2090         display_patch_changes ();
2091 }
2092
2093 void
2094 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
2095 {
2096         trackview.editor().begin_reversible_command (_("move patch change"));
2097         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2098         c->change_time (pc.patch (), t);
2099         _model->apply_command (*trackview.session(), c);
2100         trackview.editor().commit_reversible_command ();
2101
2102         display_patch_changes ();
2103 }
2104
2105 void
2106 MidiRegionView::delete_patch_change (PatchChange* pc)
2107 {
2108         trackview.editor().begin_reversible_command (_("delete patch change"));
2109
2110         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2111         c->remove (pc->patch ());
2112         _model->apply_command (*trackview.session(), c);
2113         trackview.editor().commit_reversible_command ();
2114
2115         remove_canvas_patch_change (pc);
2116         display_patch_changes ();
2117 }
2118
2119 void
2120 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2121 {
2122         MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2123         if (bank) {
2124                 key.set_bank(key.bank() + delta);
2125         } else {
2126                 key.set_program(key.program() + delta);
2127         }
2128         change_patch_change(patch, key);
2129 }
2130
2131 void
2132 MidiRegionView::note_deleted (NoteBase* cne)
2133 {
2134         if (_entered_note && cne == _entered_note) {
2135                 _entered_note = 0;
2136         }
2137
2138         if (_selection.empty()) {
2139                 return;
2140         }
2141
2142         _selection.erase (cne);
2143 }
2144
2145 void
2146 MidiRegionView::delete_selection()
2147 {
2148         if (_selection.empty()) {
2149                 return;
2150         }
2151
2152         if (trackview.editor().drags()->active()) {
2153                 return;
2154         }
2155
2156         start_note_diff_command (_("delete selection"));
2157
2158         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2159                 if ((*i)->selected()) {
2160                         _note_diff_command->remove((*i)->note());
2161                 }
2162         }
2163
2164         _selection.clear();
2165
2166         apply_diff ();
2167
2168         hide_verbose_cursor ();
2169 }
2170
2171 void
2172 MidiRegionView::delete_note (boost::shared_ptr<NoteType> n)
2173 {
2174         start_note_diff_command (_("delete note"));
2175         _note_diff_command->remove (n);
2176         apply_diff ();
2177
2178         hide_verbose_cursor ();
2179 }
2180
2181 void
2182 MidiRegionView::clear_editor_note_selection ()
2183 {
2184         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_editor_note_selection\n");
2185         PublicEditor& editor(trackview.editor());
2186         editor.get_selection().clear_midi_notes();
2187 }
2188
2189 void
2190 MidiRegionView::clear_selection ()
2191 {
2192         clear_selection_internal();
2193         PublicEditor& editor(trackview.editor());
2194         editor.get_selection().remove(this);
2195 }
2196
2197 void
2198 MidiRegionView::clear_selection_internal ()
2199 {
2200         DEBUG_TRACE(DEBUG::Selection, "MRV::clear_selection_internal\n");
2201
2202         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2203                 (*i)->set_selected(false);
2204                 (*i)->hide_velocity();
2205         }
2206         _selection.clear();
2207
2208         if (_entered) {
2209                 // Clearing selection entirely, ungrab keyboard
2210                 Keyboard::magic_widget_drop_focus();
2211                 _grabbed_keyboard = false;
2212         }
2213 }
2214
2215 void
2216 MidiRegionView::unique_select(NoteBase* ev)
2217 {
2218         clear_editor_note_selection();
2219         add_to_selection(ev);
2220 }
2221
2222 void
2223 MidiRegionView::select_all_notes ()
2224 {
2225         clear_editor_note_selection ();
2226
2227         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2228                 add_to_selection (i->second);
2229         }
2230 }
2231
2232 void
2233 MidiRegionView::select_range (framepos_t start, framepos_t end)
2234 {
2235         clear_editor_note_selection ();
2236
2237         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2238                 framepos_t t = source_beats_to_absolute_frames(i->first->time());
2239                 if (t >= start && t <= end) {
2240                         add_to_selection (i->second);
2241                 }
2242         }
2243 }
2244
2245 void
2246 MidiRegionView::invert_selection ()
2247 {
2248         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2249                 if (i->second->selected()) {
2250                         remove_from_selection(i->second);
2251                 } else {
2252                         add_to_selection (i->second);
2253                 }
2254         }
2255 }
2256
2257 /** Used for selection undo/redo.
2258     The requested notes most likely won't exist in the view until the next model redisplay.
2259 */
2260 void
2261 MidiRegionView::select_notes (list<Evoral::event_id_t> notes)
2262 {
2263         NoteBase* cne;
2264         list<Evoral::event_id_t>::iterator n;
2265
2266         for (n = notes.begin(); n != notes.end(); ++n) {
2267                 if ((cne = find_canvas_note(*n)) != 0) {
2268                         add_to_selection (cne);
2269                 } else {
2270                         _pending_note_selection.insert(*n);
2271                 }
2272         }
2273 }
2274
2275 void
2276 MidiRegionView::select_matching_notes (uint8_t notenum, uint16_t channel_mask, bool add, bool extend)
2277 {
2278         bool have_selection = !_selection.empty();
2279         uint8_t low_note = 127;
2280         uint8_t high_note = 0;
2281         MidiModel::Notes& notes (_model->notes());
2282         _optimization_iterator = _events.begin();
2283
2284         if (extend && !have_selection) {
2285                 extend = false;
2286         }
2287
2288         /* scan existing selection to get note range */
2289
2290         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2291                 if ((*i)->note()->note() < low_note) {
2292                         low_note = (*i)->note()->note();
2293                 }
2294                 if ((*i)->note()->note() > high_note) {
2295                         high_note = (*i)->note()->note();
2296                 }
2297         }
2298
2299         if (!add) {
2300                 clear_editor_note_selection ();
2301
2302                 if (!extend && (low_note == high_note) && (high_note == notenum)) {
2303                         /* only note previously selected is the one we are
2304                          * reselecting. treat this as cancelling the selection.
2305                          */
2306                         return;
2307                 }
2308         }
2309
2310         if (extend) {
2311                 low_note = min (low_note, notenum);
2312                 high_note = max (high_note, notenum);
2313         }
2314
2315         _no_sound_notes = true;
2316
2317         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2318
2319                 boost::shared_ptr<NoteType> note (*n);
2320                 NoteBase* cne;
2321                 bool select = false;
2322
2323                 if (((1 << note->channel()) & channel_mask) != 0) {
2324                         if (extend) {
2325                                 if ((note->note() >= low_note && note->note() <= high_note)) {
2326                                         select = true;
2327                                 }
2328                         } else if (note->note() == notenum) {
2329                                 select = true;
2330                         }
2331                 }
2332
2333                 if (select) {
2334                         if ((cne = find_canvas_note (note)) != 0) {
2335                                 // extend is false because we've taken care of it,
2336                                 // since it extends by time range, not pitch.
2337                                 note_selected (cne, add, false);
2338                         }
2339                 }
2340
2341                 add = true; // we need to add all remaining matching notes, even if the passed in value was false (for "set")
2342
2343         }
2344
2345         _no_sound_notes = false;
2346 }
2347
2348 void
2349 MidiRegionView::toggle_matching_notes (uint8_t notenum, uint16_t channel_mask)
2350 {
2351         MidiModel::Notes& notes (_model->notes());
2352         _optimization_iterator = _events.begin();
2353
2354         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
2355
2356                 boost::shared_ptr<NoteType> note (*n);
2357                 NoteBase* cne;
2358
2359                 if (note->note() == notenum && (((0x0001 << note->channel()) & channel_mask) != 0)) {
2360                         if ((cne = find_canvas_note (note)) != 0) {
2361                                 if (cne->selected()) {
2362                                         note_deselected (cne);
2363                                 } else {
2364                                         note_selected (cne, true, false);
2365                                 }
2366                         }
2367                 }
2368         }
2369 }
2370
2371 void
2372 MidiRegionView::note_selected (NoteBase* ev, bool add, bool extend)
2373 {
2374         if (!add) {
2375                 clear_editor_note_selection();
2376                 add_to_selection (ev);
2377         }
2378
2379         if (!extend) {
2380
2381                 if (!ev->selected()) {
2382                         add_to_selection (ev);
2383                 }
2384
2385         } else {
2386                 /* find end of latest note selected, select all between that and the start of "ev" */
2387
2388                 Evoral::Beats earliest = Evoral::MaxBeats;
2389                 Evoral::Beats latest   = Evoral::Beats();
2390
2391                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2392                         if ((*i)->note()->end_time() > latest) {
2393                                 latest = (*i)->note()->end_time();
2394                         }
2395                         if ((*i)->note()->time() < earliest) {
2396                                 earliest = (*i)->note()->time();
2397                         }
2398                 }
2399
2400                 if (ev->note()->end_time() > latest) {
2401                         latest = ev->note()->end_time();
2402                 }
2403
2404                 if (ev->note()->time() < earliest) {
2405                         earliest = ev->note()->time();
2406                 }
2407
2408                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2409
2410                         /* find notes entirely within OR spanning the earliest..latest range */
2411
2412                         if ((i->first->time() >= earliest && i->first->end_time() <= latest) ||
2413                             (i->first->time() <= earliest && i->first->end_time() >= latest)) {
2414                                 add_to_selection (i->second);
2415                         }
2416                 }
2417         }
2418 }
2419
2420 void
2421 MidiRegionView::note_deselected(NoteBase* ev)
2422 {
2423         remove_from_selection (ev);
2424 }
2425
2426 void
2427 MidiRegionView::update_drag_selection(framepos_t start, framepos_t end, double gy0, double gy1, bool extend)
2428 {
2429         PublicEditor& editor = trackview.editor();
2430
2431         // Convert to local coordinates
2432         const framepos_t p  = _region->position();
2433         const double     y  = midi_view()->y_position();
2434         const double     x0 = editor.sample_to_pixel(max((framepos_t)0, start - p));
2435         const double     x1 = editor.sample_to_pixel(max((framepos_t)0, end - p));
2436         const double     y0 = max(0.0, gy0 - y);
2437         const double     y1 = max(0.0, gy1 - y);
2438
2439         // TODO: Make this faster by storing the last updated selection rect, and only
2440         // adjusting things that are in the area that appears/disappeared.
2441         // We probably need a tree to be able to find events in O(log(n)) time.
2442
2443         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2444                 if (i->second->x0() < x1 && i->second->x1() > x0 && i->second->y0() < y1 && i->second->y1() > y0) {
2445                         // Rectangles intersect
2446                         if (!i->second->selected()) {
2447                                 add_to_selection (i->second);
2448                         }
2449                 } else if (i->second->selected() && !extend) {
2450                         // Rectangles do not intersect
2451                         remove_from_selection (i->second);
2452                 }
2453         }
2454
2455         typedef RouteTimeAxisView::AutomationTracks ATracks;
2456         typedef std::list<Selectable*>              Selectables;
2457
2458         /* Add control points to selection. */
2459         const ATracks& atracks = midi_view()->automation_tracks();
2460         Selectables    selectables;
2461         editor.get_selection().clear_points();
2462         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
2463                 a->second->get_selectables(start, end, gy0, gy1, selectables);
2464                 for (Selectables::const_iterator s = selectables.begin(); s != selectables.end(); ++s) {
2465                         ControlPoint* cp = dynamic_cast<ControlPoint*>(*s);
2466                         if (cp) {
2467                                 editor.get_selection().add(cp);
2468                         }
2469                 }
2470                 a->second->set_selected_points(editor.get_selection().points);
2471         }
2472 }
2473
2474 void
2475 MidiRegionView::update_vertical_drag_selection (double y1, double y2, bool extend)
2476 {
2477         if (y1 > y2) {
2478                 swap (y1, y2);
2479         }
2480
2481         // TODO: Make this faster by storing the last updated selection rect, and only
2482         // adjusting things that are in the area that appears/disappeared.
2483         // We probably need a tree to be able to find events in O(log(n)) time.
2484
2485         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
2486                 if ((i->second->y1() >= y1 && i->second->y1() <= y2)) {
2487                         // within y- (note-) range
2488                         if (!i->second->selected()) {
2489                                 add_to_selection (i->second);
2490                         }
2491                 } else if (i->second->selected() && !extend) {
2492                         remove_from_selection (i->second);
2493                 }
2494         }
2495 }
2496
2497 void
2498 MidiRegionView::remove_from_selection (NoteBase* ev)
2499 {
2500         Selection::iterator i = _selection.find (ev);
2501
2502         if (i != _selection.end()) {
2503                 _selection.erase (i);
2504                 if (_selection.empty() && _grabbed_keyboard) {
2505                         // Ungrab keyboard
2506                         Keyboard::magic_widget_drop_focus();
2507                         _grabbed_keyboard = false;
2508                 }
2509         }
2510
2511         ev->set_selected (false);
2512         ev->hide_velocity ();
2513
2514         if (_selection.empty()) {
2515                 PublicEditor& editor (trackview.editor());
2516                 editor.get_selection().remove (this);
2517         }
2518 }
2519
2520 void
2521 MidiRegionView::add_to_selection (NoteBase* ev)
2522 {
2523         const bool selection_was_empty = _selection.empty();
2524
2525         if (_selection.insert (ev).second) {
2526                 ev->set_selected (true);
2527                 start_playing_midi_note ((ev)->note());
2528                 if (selection_was_empty && _entered) {
2529                         // Grab keyboard for moving notes with arrow keys
2530                         Keyboard::magic_widget_grab_focus();
2531                         _grabbed_keyboard = true;
2532                 }
2533         }
2534
2535         if (selection_was_empty) {
2536                 PublicEditor& editor (trackview.editor());
2537                 editor.get_selection().add (this);
2538         }
2539 }
2540
2541 void
2542 MidiRegionView::move_selection(double dx, double dy, double cumulative_dy)
2543 {
2544         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2545         PossibleChord to_play;
2546         Evoral::Beats earliest = Evoral::MaxBeats;
2547
2548         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2549                 if ((*i)->note()->time() < earliest) {
2550                         earliest = (*i)->note()->time();
2551                 }
2552         }
2553
2554         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2555                 if ((*i)->note()->time() == earliest) {
2556                         to_play.push_back ((*i)->note());
2557                 }
2558                 (*i)->move_event(dx, dy);
2559         }
2560
2561         if (dy && !_selection.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2562
2563                 if (to_play.size() > 1) {
2564
2565                         PossibleChord shifted;
2566
2567                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2568                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2569                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2570                                 shifted.push_back (moved_note);
2571                         }
2572
2573                         start_playing_midi_chord (shifted);
2574
2575                 } else if (!to_play.empty()) {
2576
2577                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2578                         moved_note->set_note (moved_note->note() + cumulative_dy);
2579                         start_playing_midi_note (moved_note);
2580                 }
2581         }
2582 }
2583
2584 NoteBase*
2585 MidiRegionView::copy_selection ()
2586 {
2587         NoteBase* note;
2588         _copy_drag_events.clear ();
2589
2590         if (_selection.empty()) {
2591                 return 0;
2592         }
2593
2594         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2595                 boost::shared_ptr<NoteType> g (new NoteType (*((*i)->note())));
2596                 if (midi_view()->note_mode() == Sustained) {
2597                         Note* n = new Note (*this, _note_group, g);
2598                         update_sustained (n, false);
2599                         note = n;
2600                 } else {
2601                         Hit* h = new Hit (*this, _note_group, 10, g);
2602                         update_hit (h, false);
2603                         note = h;
2604                 }
2605
2606                 _copy_drag_events.push_back (note);
2607         }
2608
2609         return _copy_drag_events.front ();
2610 }
2611
2612 void
2613 MidiRegionView::move_copies (double dx, double dy, double cumulative_dy)
2614 {
2615         typedef vector<boost::shared_ptr<NoteType> > PossibleChord;
2616         PossibleChord to_play;
2617         Evoral::Beats earliest = Evoral::MaxBeats;
2618
2619         for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2620                 if ((*i)->note()->time() < earliest) {
2621                         earliest = (*i)->note()->time();
2622                 }
2623         }
2624
2625         for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2626                 if ((*i)->note()->time() == earliest) {
2627                         to_play.push_back ((*i)->note());
2628                 }
2629                 (*i)->move_event(dx, dy);
2630         }
2631
2632         if (dy && !_copy_drag_events.empty() && !_no_sound_notes && UIConfiguration::instance().get_sound_midi_notes()) {
2633
2634                 if (to_play.size() > 1) {
2635
2636                         PossibleChord shifted;
2637
2638                         for (PossibleChord::iterator n = to_play.begin(); n != to_play.end(); ++n) {
2639                                 boost::shared_ptr<NoteType> moved_note (new NoteType (**n));
2640                                 moved_note->set_note (moved_note->note() + cumulative_dy);
2641                                 shifted.push_back (moved_note);
2642                         }
2643
2644                         start_playing_midi_chord (shifted);
2645
2646                 } else if (!to_play.empty()) {
2647
2648                         boost::shared_ptr<NoteType> moved_note (new NoteType (*to_play.front()));
2649                         moved_note->set_note (moved_note->note() + cumulative_dy);
2650                         start_playing_midi_note (moved_note);
2651                 }
2652         }
2653 }
2654
2655 void
2656 MidiRegionView::note_dropped(NoteBase *, frameoffset_t dt, int8_t dnote, bool copy)
2657 {
2658         uint8_t lowest_note_in_selection  = 127;
2659         uint8_t highest_note_in_selection = 0;
2660         uint8_t highest_note_difference   = 0;
2661
2662         if (!copy) {
2663                 // find highest and lowest notes first
2664
2665                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2666                         uint8_t pitch = (*i)->note()->note();
2667                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2668                         highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2669                 }
2670
2671                 /*
2672                   cerr << "dnote: " << (int) dnote << endl;
2673                   cerr << "lowest note (streamview): " << int(midi_stream_view()->lowest_note())
2674                   << " highest note (streamview): " << int(midi_stream_view()->highest_note()) << endl;
2675                   cerr << "lowest note (selection): " << int(lowest_note_in_selection) << " highest note(selection): "
2676                   << int(highest_note_in_selection) << endl;
2677                   cerr << "selection size: " << _selection.size() << endl;
2678                   cerr << "Highest note in selection: " << (int) highest_note_in_selection << endl;
2679                 */
2680
2681                 // Make sure the note pitch does not exceed the MIDI standard range
2682                 if (highest_note_in_selection + dnote > 127) {
2683                         highest_note_difference = highest_note_in_selection - 127;
2684                 }
2685                 TempoMap& map (trackview.session()->tempo_map());
2686
2687                 start_note_diff_command (_("move notes"));
2688
2689                 for (Selection::iterator i = _selection.begin(); i != _selection.end() ; ++i) {
2690
2691                         double const start_qn = _region->quarter_note() - midi_region()->start_beats();
2692                         framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt;
2693                         Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn);
2694                         if (new_time < 0) {
2695                                 continue;
2696                         }
2697
2698                         note_diff_add_change (*i, MidiModel::NoteDiffCommand::StartTime, new_time);
2699
2700                         uint8_t original_pitch = (*i)->note()->note();
2701                         uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2702
2703                         // keep notes in standard midi range
2704                         clamp_to_0_127(new_pitch);
2705
2706                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2707                         highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2708
2709                         note_diff_add_change (*i, MidiModel::NoteDiffCommand::NoteNumber, new_pitch);
2710                 }
2711         } else {
2712
2713                 clear_editor_note_selection ();
2714
2715                 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end(); ++i) {
2716                         uint8_t pitch = (*i)->note()->note();
2717                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  pitch);
2718                         highest_note_in_selection = std::max(highest_note_in_selection, pitch);
2719                 }
2720
2721                 // Make sure the note pitch does not exceed the MIDI standard range
2722                 if (highest_note_in_selection + dnote > 127) {
2723                         highest_note_difference = highest_note_in_selection - 127;
2724                 }
2725
2726                 TempoMap& map (trackview.session()->tempo_map());
2727                 start_note_diff_command (_("copy notes"));
2728
2729                 for (CopyDragEvents::iterator i = _copy_drag_events.begin(); i != _copy_drag_events.end() ; ++i) {
2730
2731                         /* update time */
2732
2733                         double const start_qn = _region->quarter_note() - midi_region()->start_beats();
2734                         framepos_t new_frames = map.frame_at_quarter_note (start_qn + (*i)->note()->time().to_double()) + dt;
2735                         Evoral::Beats new_time = Evoral::Beats (map.quarter_note_at_frame (new_frames) - start_qn);
2736
2737                         if (new_time < 0) {
2738                                 continue;
2739                         }
2740
2741                         (*i)->note()->set_time (new_time);
2742
2743                         /* update pitch */
2744
2745                         uint8_t original_pitch = (*i)->note()->note();
2746                         uint8_t new_pitch      = original_pitch + dnote - highest_note_difference;
2747
2748                         (*i)->note()->set_note (new_pitch);
2749
2750                         // keep notes in standard midi range
2751                         clamp_to_0_127(new_pitch);
2752
2753                         lowest_note_in_selection  = std::min(lowest_note_in_selection,  new_pitch);
2754                         highest_note_in_selection = std::max(highest_note_in_selection, new_pitch);
2755
2756                         note_diff_add_note ((*i)->note(), true);
2757
2758                         delete *i;
2759                 }
2760
2761                 _copy_drag_events.clear ();
2762         }
2763
2764         apply_diff (false, copy);
2765
2766         // care about notes being moved beyond the upper/lower bounds on the canvas
2767         if (lowest_note_in_selection  < midi_stream_view()->lowest_note() ||
2768             highest_note_in_selection > midi_stream_view()->highest_note()) {
2769                 midi_stream_view()->set_note_range (MidiStreamView::ContentsRange);
2770         }
2771 }
2772
2773 /** @param x Pixel relative to the region position.
2774  *  @param ensure_snap defaults to false. true = snap always, ignoring snap mode and magnetic snap.
2775  *  Used for inverting the snap logic with key modifiers and snap delta calculation.
2776  *  @return Snapped frame relative to the region position.
2777  */
2778 framepos_t
2779 MidiRegionView::snap_pixel_to_sample(double x, bool ensure_snap)
2780 {
2781         PublicEditor& editor (trackview.editor());
2782         return snap_frame_to_frame (editor.pixel_to_sample (x), ensure_snap);
2783 }
2784
2785 /** @param x Pixel relative to the region position.
2786  *  @param ensure_snap defaults to false. true = ignore magnetic snap and snap mode (used for snap delta calculation).
2787  *  @return Snapped pixel relative to the region position.
2788  */
2789 double
2790 MidiRegionView::snap_to_pixel(double x, bool ensure_snap)
2791 {
2792         return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x, ensure_snap));
2793 }
2794
2795 double
2796 MidiRegionView::get_position_pixels()
2797 {
2798         framepos_t region_frame = get_position();
2799         return trackview.editor().sample_to_pixel(region_frame);
2800 }
2801
2802 double
2803 MidiRegionView::get_end_position_pixels()
2804 {
2805         framepos_t frame = get_position() + get_duration ();
2806         return trackview.editor().sample_to_pixel(frame);
2807 }
2808
2809 framepos_t
2810 MidiRegionView::source_beats_to_absolute_frames(Evoral::Beats beats) const
2811 {
2812         /* the time converter will return the frame corresponding to `beats'
2813            relative to the start of the source. The start of the source
2814            is an implied position given by region->position - region->start
2815         */
2816         const framepos_t source_start = _region->position() - _region->start();
2817         return  source_start +  _source_relative_time_converter.to (beats);
2818 }
2819
2820 Evoral::Beats
2821 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2822 {
2823         /* the `frames' argument needs to be converted into a frame count
2824            relative to the start of the source before being passed in to the
2825            converter.
2826         */
2827         const framepos_t source_start = _region->position() - _region->start();
2828         return  _source_relative_time_converter.from (frames - source_start);
2829 }
2830
2831 framepos_t
2832 MidiRegionView::region_beats_to_region_frames(Evoral::Beats beats) const
2833 {
2834         return _region_relative_time_converter.to(beats);
2835 }
2836
2837 Evoral::Beats
2838 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2839 {
2840         return _region_relative_time_converter.from(frames);
2841 }
2842
2843 double
2844 MidiRegionView::region_frames_to_region_beats_double (framepos_t frames) const
2845 {
2846         return _region_relative_time_converter_double.from(frames);
2847 }
2848
2849 void
2850 MidiRegionView::begin_resizing (bool /*at_front*/)
2851 {
2852         _resize_data.clear();
2853
2854         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2855                 Note *note = dynamic_cast<Note*> (*i);
2856
2857                 // only insert CanvasNotes into the map
2858                 if (note) {
2859                         NoteResizeData *resize_data = new NoteResizeData();
2860                         resize_data->note = note;
2861
2862                         // create a new SimpleRect from the note which will be the resize preview
2863                         ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group,
2864                                                                                             ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2865
2866                         // calculate the colors: get the color settings
2867                         uint32_t fill_color = UINT_RGBA_CHANGE_A(
2868                                 UIConfiguration::instance().color ("midi note selected"),
2869                                 128);
2870
2871                         // make the resize preview notes more transparent and bright
2872                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2873
2874                         // calculate color based on note velocity
2875                         resize_rect->set_fill_color (UINT_INTERPOLATE(
2876                                 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2877                                 fill_color,
2878                                 0.85));
2879
2880                         resize_rect->set_outline_color (NoteBase::calculate_outline (
2881                                                                 UIConfiguration::instance().color ("midi note selected")));
2882
2883                         resize_data->resize_rect = resize_rect;
2884                         _resize_data.push_back(resize_data);
2885                 }
2886         }
2887 }
2888
2889 /** Update resizing notes while user drags.
2890  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2891  * @param at_front which end of the note (true == note on, false == note off)
2892  * @param delta_x change in mouse position since the start of the drag
2893  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2894  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2895  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2896  * as the \a primary note.
2897  * @param snap_delta snap offset of the primary note in pixels. used in SnapRelative SnapDelta mode.
2898  * @param with_snap true if snap is to be used to determine the position, false if no snap is to be used.
2899  */
2900 void
2901 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
2902 {
2903         TempoMap& tmap (trackview.session()->tempo_map());
2904         bool cursor_set = false;
2905         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
2906
2907         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2908                 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2909                 Note* canvas_note = (*i)->note;
2910                 double current_x;
2911
2912                 if (at_front) {
2913                         if (relative) {
2914                                 current_x = canvas_note->x0() + delta_x + snap_delta;
2915                         } else {
2916                                 current_x = primary->x0() + delta_x + snap_delta;
2917                         }
2918                 } else {
2919                         if (relative) {
2920                                 current_x = canvas_note->x1() + delta_x + snap_delta;
2921                         } else {
2922                                 current_x = primary->x1() + delta_x + snap_delta;
2923                         }
2924                 }
2925
2926                 if (current_x < 0) {
2927                         // This works even with snapping because RegionView::snap_frame_to_frame()
2928                         // snaps forward if the snapped sample is before the beginning of the region
2929                         current_x = 0;
2930                 }
2931                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2932                         current_x = trackview.editor().sample_to_pixel(_region->length());
2933                 }
2934
2935                 if (at_front) {
2936                         if (with_snap) {
2937                                 resize_rect->set_x0 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
2938                         } else {
2939                                 resize_rect->set_x0 (current_x - snap_delta);
2940                         }
2941                         resize_rect->set_x1 (canvas_note->x1());
2942                 } else {
2943                         if (with_snap) {
2944                                 resize_rect->set_x1 (snap_to_pixel (current_x, ensure_snap) - snap_delta);
2945                         } else {
2946                                 resize_rect->set_x1 (current_x - snap_delta);
2947                         }
2948                         resize_rect->set_x0 (canvas_note->x0());
2949                 }
2950
2951                 if (!cursor_set) {
2952                         /* Convert snap delta from pixels to beats. */
2953                         framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
2954                         double snap_delta_beats = 0.0;
2955                         int sign = 1;
2956
2957                         /* negative beat offsets aren't allowed */
2958                         if (snap_delta_samps > 0) {
2959                                 snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
2960                         } else if (snap_delta_samps < 0) {
2961                                 snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
2962                                 sign = -1;
2963                         }
2964
2965                         double  snapped_x;
2966                         int32_t divisions = 0;
2967
2968                         if (with_snap) {
2969                                 snapped_x = snap_pixel_to_sample (current_x, ensure_snap);
2970                                 divisions = trackview.editor().get_grid_music_divisions (0);
2971                         } else {
2972                                 snapped_x = trackview.editor ().pixel_to_sample (current_x);
2973                         }
2974                         const Evoral::Beats beats = Evoral::Beats (tmap.exact_beat_at_frame (snapped_x + midi_region()->position(), divisions)
2975                                                                      - midi_region()->beat()) + midi_region()->start_beats();
2976
2977                         Evoral::Beats len         = Evoral::Beats();
2978
2979                         if (at_front) {
2980                                 if (beats < canvas_note->note()->end_time()) {
2981                                         len = canvas_note->note()->time() - beats + (sign * snap_delta_beats);
2982                                         len += canvas_note->note()->length();
2983                                 }
2984                         } else {
2985                                 if (beats >= canvas_note->note()->time()) {
2986                                         len = beats - canvas_note->note()->time() - (sign * snap_delta_beats);
2987                                 }
2988                         }
2989
2990                         len = std::max(Evoral::Beats(1 / 512.0), len);
2991
2992                         char buf[16];
2993                         snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2994                         show_verbose_cursor (buf, 0, 0);
2995
2996                         cursor_set = true;
2997                 }
2998
2999         }
3000 }
3001
3002
3003 /** Finish resizing notes when the user releases the mouse button.
3004  *  Parameters the same as for \a update_resizing().
3005  */
3006 void
3007 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative, double snap_delta, bool with_snap)
3008 {
3009         _note_diff_command = _model->new_note_diff_command (_("resize notes"));
3010         TempoMap& tmap (trackview.session()->tempo_map());
3011
3012         /* XX why doesn't snap_pixel_to_sample() handle this properly? */
3013         bool const ensure_snap = trackview.editor().snap_mode () != SnapMagnetic;
3014
3015         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
3016                 Note*  canvas_note = (*i)->note;
3017                 ArdourCanvas::Rectangle*  resize_rect = (*i)->resize_rect;
3018
3019                 /* Get the new x position for this resize, which is in pixels relative
3020                  * to the region position.
3021                  */
3022
3023                 double current_x;
3024
3025                 if (at_front) {
3026                         if (relative) {
3027                                 current_x = canvas_note->x0() + delta_x + snap_delta;
3028                         } else {
3029                                 current_x = primary->x0() + delta_x + snap_delta;
3030                         }
3031                 } else {
3032                         if (relative) {
3033                                 current_x = canvas_note->x1() + delta_x + snap_delta;
3034                         } else {
3035                                 current_x = primary->x1() + delta_x + snap_delta;
3036                         }
3037                 }
3038
3039                 if (current_x < 0) {
3040                         current_x = 0;
3041                 }
3042                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
3043                         current_x = trackview.editor().sample_to_pixel(_region->length());
3044                 }
3045
3046                 /* Convert snap delta from pixels to beats with sign. */
3047                 framepos_t snap_delta_samps = trackview.editor().pixel_to_sample (snap_delta);
3048                 double snap_delta_beats = 0.0;
3049                 int sign = 1;
3050
3051                 if (snap_delta_samps > 0) {
3052                         snap_delta_beats = region_frames_to_region_beats_double (snap_delta_samps);
3053                 } else if (snap_delta_samps < 0) {
3054                         snap_delta_beats = region_frames_to_region_beats_double ( - snap_delta_samps);
3055                         sign = -1;
3056                 }
3057
3058                 uint32_t divisions = 0;
3059                 /* Convert the new x position to a frame within the source */
3060                 framepos_t current_fr;
3061                 if (with_snap) {
3062                         current_fr = snap_pixel_to_sample (current_x, ensure_snap);
3063                         divisions = trackview.editor().get_grid_music_divisions (0);
3064                 } else {
3065                         current_fr = trackview.editor().pixel_to_sample (current_x);
3066                 }
3067
3068                 /* and then to beats */
3069                 const double e_qaf = tmap.exact_qn_at_frame (current_fr + midi_region()->position(), divisions);
3070                 const double quarter_note_start = _region->quarter_note() - midi_region()->start_beats();
3071                 const Evoral::Beats x_beats = Evoral::Beats (e_qaf - quarter_note_start);
3072
3073                 if (at_front && x_beats < canvas_note->note()->end_time()) {
3074                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats - (sign * snap_delta_beats));
3075                         Evoral::Beats len = canvas_note->note()->time() - x_beats + (sign * snap_delta_beats);
3076                         len += canvas_note->note()->length();
3077
3078                         if (!!len) {
3079                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
3080                         }
3081                 }
3082
3083                 if (!at_front) {
3084                         Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
3085                                                      x_beats - canvas_note->note()->time() - (sign * snap_delta_beats));
3086                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
3087                 }
3088
3089                 delete resize_rect;
3090                 delete (*i);
3091         }
3092
3093         _resize_data.clear();
3094         apply_diff(true);
3095 }
3096
3097 void
3098 MidiRegionView::abort_resizing ()
3099 {
3100         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
3101                 delete (*i)->resize_rect;
3102                 delete *i;
3103         }
3104
3105         _resize_data.clear ();
3106 }
3107
3108 void
3109 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
3110 {
3111         uint8_t new_velocity;
3112
3113         if (relative) {
3114                 new_velocity = event->note()->velocity() + velocity;
3115                 clamp_to_0_127(new_velocity);
3116         } else {
3117                 new_velocity = velocity;
3118         }
3119
3120         event->set_selected (event->selected()); // change color
3121
3122         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
3123 }
3124
3125 void
3126 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
3127 {
3128         uint8_t new_note;
3129
3130         if (relative) {
3131                 new_note = event->note()->note() + note;
3132         } else {
3133                 new_note = note;
3134         }
3135
3136         clamp_to_0_127 (new_note);
3137         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
3138 }
3139
3140 void
3141 MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::Beats end_delta)
3142 {
3143         bool change_start = false;
3144         bool change_length = false;
3145         Evoral::Beats new_start;
3146         Evoral::Beats new_length;
3147
3148         /* NOTE: the semantics of the two delta arguments are slightly subtle:
3149
3150            front_delta: if positive - move the start of the note later in time (shortening it)
3151            if negative - move the start of the note earlier in time (lengthening it)
3152
3153            end_delta:   if positive - move the end of the note later in time (lengthening it)
3154            if negative - move the end of the note earlier in time (shortening it)
3155         */
3156
3157         if (!!front_delta) {
3158                 if (front_delta < 0) {
3159
3160                         if (event->note()->time() < -front_delta) {
3161                                 new_start = Evoral::Beats();
3162                         } else {
3163                                 new_start = event->note()->time() + front_delta; // moves earlier
3164                         }
3165
3166                         /* start moved toward zero, so move the end point out to where it used to be.
3167                            Note that front_delta is negative, so this increases the length.
3168                         */
3169
3170                         new_length = event->note()->length() - front_delta;
3171                         change_start = true;
3172                         change_length = true;
3173
3174                 } else {
3175
3176                         Evoral::Beats new_pos = event->note()->time() + front_delta;
3177
3178                         if (new_pos < event->note()->end_time()) {
3179                                 new_start = event->note()->time() + front_delta;
3180                                 /* start moved toward the end, so move the end point back to where it used to be */
3181                                 new_length = event->note()->length() - front_delta;
3182                                 change_start = true;
3183                                 change_length = true;
3184                         }
3185                 }
3186
3187         }
3188
3189         if (!!end_delta) {
3190                 bool can_change = true;
3191                 if (end_delta < 0) {
3192                         if (event->note()->length() < -end_delta) {
3193                                 can_change = false;
3194                         }
3195                 }
3196
3197                 if (can_change) {
3198                         new_length = event->note()->length() + end_delta;
3199                         change_length = true;
3200                 }
3201         }
3202
3203         if (change_start) {
3204                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
3205         }
3206
3207         if (change_length) {
3208                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
3209         }
3210 }
3211
3212 void
3213 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
3214 {
3215         uint8_t new_channel;
3216
3217         if (relative) {
3218                 if (chn < 0.0) {
3219                         if (event->note()->channel() < -chn) {
3220                                 new_channel = 0;
3221                         } else {
3222                                 new_channel = event->note()->channel() + chn;
3223                         }
3224                 } else {
3225                         new_channel = event->note()->channel() + chn;
3226                 }
3227         } else {
3228                 new_channel = (uint8_t) chn;
3229         }
3230
3231         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
3232 }
3233
3234 void
3235 MidiRegionView::change_note_time (NoteBase* event, Evoral::Beats delta, bool relative)
3236 {
3237         Evoral::Beats new_time;
3238
3239         if (relative) {
3240                 if (delta < 0.0) {
3241                         if (event->note()->time() < -delta) {
3242                                 new_time = Evoral::Beats();
3243                         } else {
3244                                 new_time = event->note()->time() + delta;
3245                         }
3246                 } else {
3247                         new_time = event->note()->time() + delta;
3248                 }
3249         } else {
3250                 new_time = delta;
3251         }
3252
3253         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3254 }
3255
3256 void
3257 MidiRegionView::change_note_length (NoteBase* event, Evoral::Beats t)
3258 {
3259         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3260 }
3261
3262 void
3263 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3264 {
3265         int8_t delta;
3266         int8_t value = 0;
3267
3268         if (_selection.empty()) {
3269                 return;
3270         }
3271
3272         if (fine) {
3273                 delta = 1;
3274         } else {
3275                 delta = 10;
3276         }
3277
3278         if (!up) {
3279                 delta = -delta;
3280         }
3281
3282         if (!allow_smush) {
3283                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3284                         if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3285                                 goto cursor_label;
3286                         }
3287                 }
3288         }
3289
3290         start_note_diff_command (_("change velocities"));
3291
3292         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3293                 Selection::iterator next = i;
3294                 ++next;
3295
3296                 if (all_together) {
3297                         if (i == _selection.begin()) {
3298                                 change_note_velocity (*i, delta, true);
3299                                 value = (*i)->note()->velocity() + delta;
3300                         } else {
3301                                 change_note_velocity (*i, value, false);
3302                         }
3303
3304                 } else {
3305                         change_note_velocity (*i, delta, true);
3306                 }
3307
3308                 i = next;
3309         }
3310
3311         apply_diff();
3312
3313   cursor_label:
3314         if (!_selection.empty()) {
3315                 char buf[24];
3316                 snprintf (buf, sizeof (buf), "Vel %d",
3317                           (int) (*_selection.begin())->note()->velocity());
3318                 show_verbose_cursor (buf, 10, 10);
3319         }
3320 }
3321
3322
3323 void
3324 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3325 {
3326         if (_selection.empty()) {
3327                 return;
3328         }
3329
3330         int8_t delta;
3331
3332         if (fine) {
3333                 delta = 1;
3334         } else {
3335                 delta = 12;
3336         }
3337
3338         if (!up) {
3339                 delta = -delta;
3340         }
3341
3342         if (!allow_smush) {
3343                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3344                         if (!up) {
3345                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3346                                         return;
3347                                 }
3348                         } else {
3349                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
3350                                         return;
3351                                 }
3352                         }
3353                 }
3354         }
3355
3356         start_note_diff_command (_("transpose"));
3357
3358         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3359                 Selection::iterator next = i;
3360                 ++next;
3361                 change_note_note (*i, delta, true);
3362                 i = next;
3363         }
3364
3365         apply_diff ();
3366 }
3367
3368 void
3369 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::Beats delta, bool start, bool end)
3370 {
3371         if (!delta) {
3372                 if (fine) {
3373                         delta = Evoral::Beats(1.0/128.0);
3374                 } else {
3375                         /* grab the current grid distance */
3376                         delta = get_grid_beats(_region->position());
3377                 }
3378         }
3379
3380         if (shorter) {
3381                 delta = -delta;
3382         }
3383
3384         start_note_diff_command (_("change note lengths"));
3385
3386         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3387                 Selection::iterator next = i;
3388                 ++next;
3389
3390                 /* note the negation of the delta for start */
3391
3392                 trim_note (*i,
3393                            (start ? -delta : Evoral::Beats()),
3394                            (end   ? delta  : Evoral::Beats()));
3395                 i = next;
3396         }
3397
3398         apply_diff ();
3399
3400 }
3401
3402 void
3403 MidiRegionView::nudge_notes (bool forward, bool fine)
3404 {
3405         if (_selection.empty()) {
3406                 return;
3407         }
3408
3409         /* pick a note as the point along the timeline to get the nudge distance.
3410            its not necessarily the earliest note, so we may want to pull the notes out
3411            into a vector and sort before using the first one.
3412         */
3413
3414         const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3415         Evoral::Beats    delta;
3416
3417         if (!fine) {
3418
3419                 /* non-fine, move by 1 bar regardless of snap */
3420                 delta = Evoral::Beats(trackview.session()->tempo_map().meter_at_frame (ref_point).divisions_per_bar());
3421
3422         } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3423
3424                 /* grid is off - use nudge distance */
3425
3426                 framepos_t       unused;
3427                 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3428                 delta = region_frames_to_region_beats (fabs ((double)distance));
3429
3430         } else {
3431
3432                 /* use grid */
3433
3434                 framepos_t next_pos = ref_point;
3435
3436                 if (forward) {
3437                         if (max_framepos - 1 < next_pos) {
3438                                 next_pos += 1;
3439                         }
3440                 } else {
3441                         if (next_pos == 0) {
3442                                 return;
3443                         }
3444                         next_pos -= 1;
3445                 }
3446
3447                 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3448                 const framecnt_t distance = ref_point - next_pos;
3449                 delta = region_frames_to_region_beats (fabs ((double)distance));
3450         }
3451
3452         if (!delta) {
3453                 return;
3454         }
3455
3456         if (!forward) {
3457                 delta = -delta;
3458         }
3459
3460         start_note_diff_command (_("nudge"));
3461
3462         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3463                 Selection::iterator next = i;
3464                 ++next;
3465                 change_note_time (*i, delta, true);
3466                 i = next;
3467         }
3468
3469         apply_diff ();
3470 }
3471
3472 void
3473 MidiRegionView::change_channel(uint8_t channel)
3474 {
3475         start_note_diff_command(_("change channel"));
3476         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3477                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3478         }
3479
3480         apply_diff();
3481 }
3482
3483
3484 void
3485 MidiRegionView::note_entered(NoteBase* ev)
3486 {
3487         _entered_note = ev;
3488
3489         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3490
3491         if (_mouse_state == SelectTouchDragging) {
3492
3493                 note_selected (ev, true);
3494
3495         } else if (editor->current_mouse_mode() == MouseContent) {
3496
3497                 remove_ghost_note ();
3498                 show_verbose_cursor (ev->note ());
3499
3500         } else if (editor->current_mouse_mode() == MouseDraw) {
3501
3502                 remove_ghost_note ();
3503                 show_verbose_cursor (ev->note ());
3504         }
3505 }
3506
3507 void
3508 MidiRegionView::note_left (NoteBase*)
3509 {
3510         _entered_note = 0;
3511
3512         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3513                 (*i)->hide_velocity ();
3514         }
3515
3516         hide_verbose_cursor ();
3517 }
3518
3519 void
3520 MidiRegionView::patch_entered (PatchChange* p)
3521 {
3522         ostringstream s;
3523         s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n'
3524           << instrument_info().get_patch_name_without (p->patch()->bank(), p->patch()->program(), p->patch()->channel()) << '\n'
3525           << _("Channel ") << ((int) p->patch()->channel() + 1);
3526         show_verbose_cursor (s.str(), 10, 20);
3527         p->item().grab_focus();
3528 }
3529
3530 void
3531 MidiRegionView::patch_left (PatchChange *)
3532 {
3533         hide_verbose_cursor ();
3534         /* focus will transfer back via the enter-notify event sent to this
3535          * midi region view.
3536          */
3537 }
3538
3539 void
3540 MidiRegionView::sysex_entered (SysEx* p)
3541 {
3542         // ostringstream s;
3543         // CAIROCANVAS
3544         // need a way to extract text from p->_flag->_text
3545         // s << p->text();
3546         // show_verbose_cursor (s.str(), 10, 20);
3547         p->item().grab_focus();
3548 }
3549
3550 void
3551 MidiRegionView::sysex_left (SysEx *)
3552 {
3553         hide_verbose_cursor ();
3554         /* focus will transfer back via the enter-notify event sent to this
3555          * midi region view.
3556          */
3557 }
3558
3559 void
3560 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3561 {
3562         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3563         Editing::MouseMode mm = editor->current_mouse_mode();
3564         bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3565
3566         Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3567         if (can_set_cursor && ctx) {
3568                 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3569                         ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3570                 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3571                         ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3572                 } else {
3573                         ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3574                 }
3575         }
3576 }
3577
3578 uint32_t
3579 MidiRegionView::get_fill_color() const
3580 {
3581         const std::string mod_name = (_dragging ? "dragging region" :
3582                                       trackview.editor().internal_editing() ? "editable region" :
3583                                       "midi frame base");
3584         if (_selected) {
3585                 return UIConfiguration::instance().color_mod ("selected region base", mod_name);
3586         } else if ((!UIConfiguration::instance().get_show_name_highlight() || high_enough_for_name) &&
3587                    !UIConfiguration::instance().get_color_regions_using_track_color()) {
3588                 return UIConfiguration::instance().color_mod ("midi frame base", mod_name);
3589         }
3590         return UIConfiguration::instance().color_mod (fill_color, mod_name);
3591 }
3592
3593 void
3594 MidiRegionView::midi_channel_mode_changed ()
3595 {
3596         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3597         uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3598         ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3599
3600         if (mode == ForceChannel) {
3601                 mask = 0xFFFF; // Show all notes as active (below)
3602         }
3603
3604         // Update notes for selection
3605         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3606                 i->second->on_channel_selection_change (mask);
3607         }
3608
3609         _patch_changes.clear ();
3610         display_patch_changes ();
3611 }
3612
3613 void
3614 MidiRegionView::instrument_settings_changed ()
3615 {
3616         redisplay_model();
3617 }
3618
3619 void
3620 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3621 {
3622         if (_selection.empty()) {
3623                 return;
3624         }
3625
3626         PublicEditor& editor (trackview.editor());
3627
3628         switch (op) {
3629         case Delete:
3630                 /* XXX what to do ? */
3631                 break;
3632         case Cut:
3633         case Copy:
3634                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3635                 break;
3636         default:
3637                 break;
3638         }
3639
3640         if (op != Copy) {
3641
3642                 start_note_diff_command();
3643
3644                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3645                         switch (op) {
3646                         case Copy:
3647                                 break;
3648                         case Delete:
3649                         case Cut:
3650                         case Clear:
3651                                 note_diff_remove_note (*i);
3652                                 break;
3653                         }
3654                 }
3655
3656                 apply_diff();
3657         }
3658 }
3659
3660 MidiCutBuffer*
3661 MidiRegionView::selection_as_cut_buffer () const
3662 {
3663         Notes notes;
3664
3665         for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3666                 NoteType* n = (*i)->note().get();
3667                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3668         }
3669
3670         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3671         cb->set (notes);
3672
3673         return cb;
3674 }
3675
3676 /** This method handles undo */
3677 bool
3678 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx, const int32_t sub_num)
3679 {
3680         bool commit = false;
3681         // Paste notes, if available
3682         MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3683         if (m != selection.midi_notes.end()) {
3684                 ctx.counts.increase_n_notes();
3685                 if (!(*m)->empty()) {
3686                         commit = true;
3687                 }
3688                 paste_internal(pos, ctx.count, ctx.times, **m);
3689         }
3690
3691         // Paste control points to automation children, if available
3692         typedef RouteTimeAxisView::AutomationTracks ATracks;
3693         const ATracks& atracks = midi_view()->automation_tracks();
3694         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3695                 if (a->second->paste(pos, selection, ctx, sub_num)) {
3696                         if(!commit) {
3697                                 trackview.editor().begin_reversible_command (Operations::paste);
3698                         }
3699                         commit = true;
3700                 }
3701         }
3702
3703         if (commit) {
3704                 trackview.editor().commit_reversible_command ();
3705         }
3706         return true;
3707 }
3708
3709 /** This method handles undo */
3710 void
3711 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3712 {
3713         if (mcb.empty()) {
3714                 return;
3715         }
3716
3717         start_note_diff_command (_("paste"));
3718
3719         const Evoral::Beats snap_beats    = get_grid_beats(pos);
3720         const Evoral::Beats first_time    = (*mcb.notes().begin())->time();
3721         const Evoral::Beats last_time     = (*mcb.notes().rbegin())->end_time();
3722         const Evoral::Beats duration      = last_time - first_time;
3723         const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
3724         const Evoral::Beats paste_offset  = snap_duration * paste_count;
3725         const Evoral::Beats quarter_note     = absolute_frames_to_source_beats(pos) + paste_offset;
3726         Evoral::Beats       end_point     = Evoral::Beats();
3727
3728         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3729                                                        first_time,
3730                                                        last_time,
3731                                                        duration, pos, _region->position(),
3732                                                        quarter_note));
3733
3734         clear_editor_note_selection ();
3735
3736         for (int n = 0; n < (int) times; ++n) {
3737
3738                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3739
3740                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3741                         copied_note->set_time (quarter_note + copied_note->time() - first_time);
3742                         copied_note->set_id (Evoral::next_event_id());
3743
3744                         /* make all newly added notes selected */
3745
3746                         note_diff_add_note (copied_note, true);
3747                         end_point = copied_note->end_time();
3748                 }
3749         }
3750
3751         /* if we pasted past the current end of the region, extend the region */
3752
3753         framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3754         framepos_t region_end = _region->position() + _region->length() - 1;
3755
3756         if (end_frame > region_end) {
3757
3758                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3759
3760                 _region->clear_changes ();
3761                 /* we probably need to get the snap modifier somehow to make this correct for non-musical use */
3762                 _region->set_length (end_frame - _region->position(), trackview.editor().get_grid_music_divisions (0));
3763                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3764         }
3765
3766         apply_diff (true);
3767 }
3768
3769 struct EventNoteTimeEarlyFirstComparator {
3770         bool operator() (NoteBase* a, NoteBase* b) {
3771                 return a->note()->time() < b->note()->time();
3772         }
3773 };
3774
3775 void
3776 MidiRegionView::goto_next_note (bool add_to_selection)
3777 {
3778         bool use_next = false;
3779
3780         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3781         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3782         NoteBase* first_note = 0;
3783
3784         MidiModel::ReadLock lock(_model->read_lock());
3785         MidiModel::Notes& notes (_model->notes());
3786
3787         for (MidiModel::Notes::iterator n = notes.begin(); n != notes.end(); ++n) {
3788                 NoteBase* cne = 0;
3789                 if ((cne = find_canvas_note (*n))) {
3790
3791                         if (!first_note && (channel_mask & (1 << (*n)->channel()))) {
3792                                 first_note = cne;
3793                         }
3794
3795                         if (cne->selected()) {
3796                                 use_next = true;
3797                                 continue;
3798                         } else if (use_next) {
3799                                 if (channel_mask & (1 << (*n)->channel())) {
3800                                         if (!add_to_selection) {
3801                                                 unique_select (cne);
3802                                         } else {
3803                                                 note_selected (cne, true, false);
3804                                         }
3805
3806                                         return;
3807                                 }
3808                         }
3809                 }
3810         }
3811
3812         /* use the first one */
3813
3814         if (!_events.empty() && first_note) {
3815                 unique_select (first_note);
3816         }
3817 }
3818
3819 void
3820 MidiRegionView::goto_previous_note (bool add_to_selection)
3821 {
3822         bool use_next = false;
3823
3824         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3825         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3826         NoteBase* last_note = 0;
3827
3828         MidiModel::ReadLock lock(_model->read_lock());
3829         MidiModel::Notes& notes (_model->notes());
3830
3831         for (MidiModel::Notes::reverse_iterator n = notes.rbegin(); n != notes.rend(); ++n) {
3832                 NoteBase* cne = 0;
3833                 if ((cne = find_canvas_note (*n))) {
3834
3835                         if (!last_note && (channel_mask & (1 << (*n)->channel()))) {
3836                                 last_note = cne;
3837                         }
3838
3839                         if (cne->selected()) {
3840                                 use_next = true;
3841                                 continue;
3842
3843                         } else if (use_next) {
3844                                 if (channel_mask & (1 << (*n)->channel())) {
3845                                         if (!add_to_selection) {
3846                                                 unique_select (cne);
3847                                         } else {
3848                                                 note_selected (cne, true, false);
3849                                         }
3850
3851                                         return;
3852                                 }
3853                         }
3854                 }
3855         }
3856
3857         /* use the last one */
3858
3859         if (!_events.empty() && last_note) {
3860                 unique_select (last_note);
3861         }
3862 }
3863
3864 void
3865 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3866 {
3867         bool had_selected = false;
3868
3869         /* we previously time sorted events here, but Notes is a multiset sorted by time */
3870
3871         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3872                 if (i->second->selected()) {
3873                         selected.insert (i->first);
3874                         had_selected = true;
3875                 }
3876         }
3877
3878         if (allow_all_if_none_selected && !had_selected) {
3879                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3880                         selected.insert (i->first);
3881                 }
3882         }
3883 }
3884
3885 void
3886 MidiRegionView::update_ghost_note (double x, double y, uint32_t state)
3887 {
3888         x = std::max(0.0, x);
3889
3890         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3891
3892         _last_ghost_x = x;
3893         _last_ghost_y = y;
3894
3895         _note_group->canvas_to_item (x, y);
3896
3897         PublicEditor& editor = trackview.editor ();
3898
3899         framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3900
3901         const int32_t divisions = editor.get_grid_music_divisions (state);
3902         const bool shift_snap = midi_view()->note_mode() != Percussive;
3903         const Evoral::Beats snapped_beats = snap_frame_to_grid_underneath (unsnapped_frame, divisions, shift_snap);
3904
3905         /* prevent Percussive mode from displaying a ghost hit at region end */
3906         if (!shift_snap && snapped_beats >= midi_region()->start_beats() + midi_region()->length_beats()) {
3907                 _ghost_note->hide();
3908                 hide_verbose_cursor ();
3909                 return;
3910         }
3911
3912         /* ghost note may have been snapped before region */
3913         if (_ghost_note && snapped_beats.to_double() < 0.0) {
3914                 _ghost_note->hide();
3915                 return;
3916
3917         } else if (_ghost_note) {
3918                 _ghost_note->show();
3919         }
3920
3921         /* calculate time in beats relative to start of source */
3922         const Evoral::Beats length = get_grid_beats(unsnapped_frame + _region->position());
3923
3924         _ghost_note->note()->set_time (snapped_beats);
3925         _ghost_note->note()->set_length (length);
3926         _ghost_note->note()->set_note (y_to_note (y));
3927         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3928         _ghost_note->note()->set_velocity (get_velocity_for_add (snapped_beats));
3929         /* the ghost note does not appear in ghost regions, so pass false in here */
3930         update_note (_ghost_note, false);
3931
3932         show_verbose_cursor (_ghost_note->note ());
3933 }
3934
3935 void
3936 MidiRegionView::create_ghost_note (double x, double y, uint32_t state)
3937 {
3938         remove_ghost_note ();
3939
3940         boost::shared_ptr<NoteType> g (new NoteType);
3941         if (midi_view()->note_mode() == Sustained) {
3942                 _ghost_note = new Note (*this, _note_group, g);
3943         } else {
3944                 _ghost_note = new Hit (*this, _note_group, 10, g);
3945         }
3946         _ghost_note->set_ignore_events (true);
3947         _ghost_note->set_outline_color (0x000000aa);
3948         update_ghost_note (x, y, state);
3949         _ghost_note->show ();
3950
3951         show_verbose_cursor (_ghost_note->note ());
3952 }
3953
3954 void
3955 MidiRegionView::remove_ghost_note ()
3956 {
3957         delete _ghost_note;
3958         _ghost_note = 0;
3959 }
3960
3961 void
3962 MidiRegionView::hide_verbose_cursor ()
3963 {
3964         trackview.editor().verbose_cursor()->hide ();
3965         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3966         if (mtv) {
3967                 mtv->set_note_highlight (NO_MIDI_NOTE);
3968         }
3969 }
3970
3971 void
3972 MidiRegionView::snap_changed ()
3973 {
3974         if (!_ghost_note) {
3975                 return;
3976         }
3977
3978         create_ghost_note (_last_ghost_x, _last_ghost_y, 0);
3979 }
3980
3981 void
3982 MidiRegionView::drop_down_keys ()
3983 {
3984         _mouse_state = None;
3985 }
3986
3987 void
3988 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3989 {
3990         /* XXX: This is dead code.  What was it for? */
3991
3992         double note = y_to_note(y);
3993         Events e;
3994         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3995
3996         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3997
3998         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3999                 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
4000         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
4001                 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
4002         } else {
4003                 return;
4004         }
4005
4006         bool add_mrv_selection = false;
4007
4008         if (_selection.empty()) {
4009                 add_mrv_selection = true;
4010         }
4011
4012         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
4013                 if (_selection.insert (i->second).second) {
4014                         i->second->set_selected (true);
4015                 }
4016         }
4017
4018         if (add_mrv_selection) {
4019                 PublicEditor& editor (trackview.editor());
4020                 editor.get_selection().add (this);
4021         }
4022 }
4023
4024 void
4025 MidiRegionView::color_handler ()
4026 {
4027         RegionView::color_handler ();
4028
4029         _patch_change_outline = UIConfiguration::instance().color ("midi patch change outline");
4030         _patch_change_fill = UIConfiguration::instance().color_mod ("midi patch change fill", "midi patch change fill");
4031
4032         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
4033                 i->second->set_selected (i->second->selected()); // will change color
4034         }
4035
4036         /* XXX probably more to do here */
4037 }
4038
4039 void
4040 MidiRegionView::enable_display (bool yn)
4041 {
4042         RegionView::enable_display (yn);
4043 }
4044
4045 void
4046 MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
4047 {
4048         if (_step_edit_cursor == 0) {
4049                 ArdourCanvas::Item* const group = get_canvas_group();
4050
4051                 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
4052                 _step_edit_cursor->set_y0 (0);
4053                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
4054                 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
4055                 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
4056         }
4057
4058         move_step_edit_cursor (pos);
4059         _step_edit_cursor->show ();
4060 }
4061
4062 void
4063 MidiRegionView::move_step_edit_cursor (Evoral::Beats pos)
4064 {
4065         _step_edit_cursor_position = pos;
4066
4067         if (_step_edit_cursor) {
4068                 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
4069                 _step_edit_cursor->set_x0 (pixel);
4070                 set_step_edit_cursor_width (_step_edit_cursor_width);
4071         }
4072 }
4073
4074 void
4075 MidiRegionView::hide_step_edit_cursor ()
4076 {
4077         if (_step_edit_cursor) {
4078                 _step_edit_cursor->hide ();
4079         }
4080 }
4081
4082 void
4083 MidiRegionView::set_step_edit_cursor_width (Evoral::Beats beats)
4084 {
4085         _step_edit_cursor_width = beats;
4086
4087         if (_step_edit_cursor) {
4088                 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (
4089                                                    region_beats_to_region_frames (_step_edit_cursor_position + beats)
4090                                                    - region_beats_to_region_frames (_step_edit_cursor_position)));
4091         }
4092 }
4093
4094 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
4095  *  @param w Source that the data will end up in.
4096  */
4097 void
4098 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
4099 {
4100         if (!_active_notes) {
4101                 /* we aren't actively being recorded to */
4102                 return;
4103         }
4104
4105         boost::shared_ptr<MidiSource> src = w.lock ();
4106         if (!src || src != midi_region()->midi_source()) {
4107                 /* recorded data was not destined for our source */
4108                 return;
4109         }
4110
4111         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
4112
4113         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
4114
4115         framepos_t back = max_framepos;
4116
4117         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
4118                 const Evoral::Event<MidiBuffer::TimeType>& ev = *i;
4119
4120                 if (ev.is_channel_event()) {
4121                         if (get_channel_mode() == FilterChannels) {
4122                                 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
4123                                         continue;
4124                                 }
4125                         }
4126                 }
4127
4128                 /* convert from session frames to source beats */
4129                 Evoral::Beats const time_beats = _source_relative_time_converter.from(
4130                         ev.time() - src->timeline_position() + _region->start());
4131
4132                 if (ev.type() == MIDI_CMD_NOTE_ON) {
4133                         boost::shared_ptr<NoteType> note (
4134                                 new NoteType (ev.channel(), time_beats, Evoral::Beats(), ev.note(), ev.velocity()));
4135
4136                         add_note (note, true);
4137
4138                         /* fix up our note range */
4139                         if (ev.note() < _current_range_min) {
4140                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
4141                         } else if (ev.note() > _current_range_max) {
4142                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
4143                         }
4144
4145                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
4146                         resolve_note (ev.note (), time_beats);
4147                 }
4148
4149                 back = ev.time ();
4150         }
4151
4152         midi_stream_view()->check_record_layers (region(), back);
4153 }
4154
4155 void
4156 MidiRegionView::trim_front_starting ()
4157 {
4158         /* We used to eparent the note group to the region view's parent, so that it didn't change.
4159            now we update it.
4160         */
4161 }
4162
4163 void
4164 MidiRegionView::trim_front_ending ()
4165 {
4166         if (_region->start() < 0) {
4167                 /* Trim drag made start time -ve; fix this */
4168                 midi_region()->fix_negative_start ();
4169         }
4170 }
4171
4172 void
4173 MidiRegionView::edit_patch_change (PatchChange* pc)
4174 {
4175         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
4176
4177         int response = d.run();
4178
4179         switch (response) {
4180         case Gtk::RESPONSE_ACCEPT:
4181                 break;
4182         case Gtk::RESPONSE_REJECT:
4183                 delete_patch_change (pc);
4184                 return;
4185         default:
4186                 return;
4187         }
4188
4189         change_patch_change (pc->patch(), d.patch ());
4190 }
4191
4192 void
4193 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
4194 {
4195         // CAIROCANVAS
4196         // sysyex object doesn't have a pointer to a sysex event
4197         // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
4198         // c->remove (sysex->sysex());
4199         // _model->apply_command (*trackview.session(), c);
4200
4201         //_sys_exes.clear ();
4202         // display_sysexes();
4203 }
4204
4205 std::string
4206 MidiRegionView::get_note_name (boost::shared_ptr<NoteType> n, uint8_t note_value) const
4207 {
4208         using namespace MIDI::Name;
4209         std::string name;
4210
4211         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4212         if (mtv) {
4213                 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
4214                 if (device_names) {
4215                         MIDI::Name::PatchPrimaryKey patch_key;
4216                         get_patch_key_at(n->time(), n->channel(), patch_key);
4217                         name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
4218                                                        n->channel(),
4219                                                        patch_key.bank(),
4220                                                        patch_key.program(),
4221                                                        note_value);
4222                 }
4223         }
4224
4225         char buf[128];
4226         snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
4227                   (int) note_value,
4228                   name.empty() ? ParameterDescriptor::midi_note_name (note_value).c_str() : name.c_str(),
4229                   (int) n->channel() + 1,
4230                   (int) n->velocity());
4231
4232         return buf;
4233 }
4234
4235 void
4236 MidiRegionView::show_verbose_cursor_for_new_note_value(boost::shared_ptr<NoteType> current_note,
4237                                                        uint8_t new_value) const
4238 {
4239         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
4240         if (mtv) {
4241                 mtv->set_note_highlight (new_value);
4242         }
4243
4244         show_verbose_cursor(get_note_name(current_note, new_value), 10, 20);
4245 }
4246
4247 void
4248 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
4249 {
4250         show_verbose_cursor_for_new_note_value(n, n->note());
4251 }
4252
4253 void
4254 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
4255 {
4256         trackview.editor().verbose_cursor()->set (text);
4257         trackview.editor().verbose_cursor()->show ();
4258         trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
4259 }
4260
4261 uint8_t
4262 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
4263 {
4264         if (_model->notes().empty()) {
4265                 return 0x40;  // No notes, use default
4266         }
4267
4268         MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
4269         if (m == _model->notes().begin()) {
4270                 // Before the start, use the velocity of the first note
4271                 return (*m)->velocity();
4272         } else if (m == _model->notes().end()) {
4273                 // Past the end, use the velocity of the last note
4274                 --m;
4275                 return (*m)->velocity();
4276         }
4277
4278         // Interpolate velocity of surrounding notes
4279         MidiModel::Notes::const_iterator n = m;
4280         --n;
4281
4282         const double frac = ((time - (*n)->time()).to_double() /
4283                              ((*m)->time() - (*n)->time()).to_double());
4284
4285         return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4286 }
4287
4288 /** @param p A session framepos.
4289  *  @param divisions beat division to snap given by Editor::get_grid_music_divisions() where
4290  *  bar is -1, 0 is audio samples and a positive integer is beat subdivisions.
4291  *  @return beat duration of p snapped to the grid subdivision underneath it.
4292  */
4293 Evoral::Beats
4294 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, int32_t divisions, bool shift_snap) const
4295 {
4296         TempoMap& map (trackview.session()->tempo_map());
4297         double eqaf = map.exact_qn_at_frame (p + _region->position(), divisions);
4298
4299         if (divisions != 0 && shift_snap) {
4300                 const double qaf = map.quarter_note_at_frame (p + _region->position());
4301                 /* Hack so that we always snap to the note that we are over, instead of snapping
4302                    to the next one if we're more than halfway through the one we're over.
4303                 */
4304                 const Evoral::Beats grid_beats = get_grid_beats (p + _region->position());
4305                 const double rem = eqaf - qaf;
4306                 if (rem >= 0.0) {
4307                         eqaf -= grid_beats.to_double();
4308                 }
4309         }
4310         const double session_start_off = _region->quarter_note() - midi_region()->start_beats();
4311
4312         return Evoral::Beats (eqaf - session_start_off);
4313 }
4314
4315 ChannelMode
4316 MidiRegionView::get_channel_mode () const
4317 {
4318         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4319         return rtav->midi_track()->get_playback_channel_mode();
4320 }
4321
4322 uint16_t
4323 MidiRegionView::get_selected_channels () const
4324 {
4325         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4326         return rtav->midi_track()->get_playback_channel_mask();
4327 }
4328
4329
4330 Evoral::Beats
4331 MidiRegionView::get_grid_beats(framepos_t pos) const
4332 {
4333         PublicEditor& editor  = trackview.editor();
4334         bool          success = false;
4335         Evoral::Beats beats   = editor.get_grid_type_as_beats (success, pos);
4336         if (!success) {
4337                 beats = Evoral::Beats(1);
4338         }
4339         return beats;
4340 }
4341 uint8_t
4342 MidiRegionView::y_to_note (double y) const
4343 {
4344         int const n = ((contents_height() - y) / contents_height() * (double)(_current_range_max - _current_range_min + 1))
4345                 + _current_range_min;
4346
4347         if (n < 0) {
4348                 return 0;
4349         } else if (n > 127) {
4350                 return 127;
4351         }
4352
4353         /* min due to rounding and/or off-by-one errors */
4354         return min ((uint8_t) n, _current_range_max);
4355 }
4356
4357 double
4358 MidiRegionView::note_to_y(uint8_t note) const
4359 {
4360         return contents_height() - (note + 1 - _current_range_min) * note_height() + 1;
4361 }