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