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