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