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