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