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