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