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