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