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