Fix mute of MIDI tracks with channel forcing.
[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 "ardour_ui.h"
80 #include "note.h"
81 #include "hit.h"
82 #include "patch_change.h"
83 #include "sys_ex.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         , _active_notes(0)
108         , _note_group (new ArdourCanvas::Container (group))
109         , _note_diff_command (0)
110         , _ghost_note(0)
111         , _step_edit_cursor (0)
112         , _step_edit_cursor_width (1.0)
113         , _step_edit_cursor_position (0.0)
114         , _channel_selection_scoped_note (0)
115         , _temporary_note_group (0)
116         , _mouse_state(None)
117         , _pressed_button(0)
118         , _sort_needed (true)
119         , _optimization_iterator (_events.end())
120         , _list_editor (0)
121         , _no_sound_notes (false)
122         , _last_display_zoom (0)
123         , _last_event_x (0)
124         , _last_event_y (0)
125         , _grabbed_keyboard (false)
126         , _entered (false)
127         , _mouse_changed_selection (false)
128 {
129         CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
130         _note_group->raise_to_top();
131         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
132
133         Config->ParameterChanged.connect (*this, invalidator (*this), boost::bind (&MidiRegionView::parameter_changed, this, _1), gui_context());
134         connect_to_diskstream ();
135
136         SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
137
138         PublicEditor& editor (trackview.editor());
139         editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
140 }
141
142 MidiRegionView::MidiRegionView (ArdourCanvas::Container*      parent,
143                                 RouteTimeAxisView&            tv,
144                                 boost::shared_ptr<MidiRegion> r,
145                                 double                        spu,
146                                 uint32_t                      basic_color,
147                                 bool                          recording,
148                                 TimeAxisViewItem::Visibility  visibility)
149         : RegionView (parent, tv, r, spu, basic_color, recording, visibility)
150         , _current_range_min(0)
151         , _current_range_max(0)
152         , _region_relative_time_converter(r->session().tempo_map(), r->position())
153         , _source_relative_time_converter(r->session().tempo_map(), r->position() - r->start())
154         , _active_notes(0)
155         , _note_group (new ArdourCanvas::Container (group))
156         , _note_diff_command (0)
157         , _ghost_note(0)
158         , _step_edit_cursor (0)
159         , _step_edit_cursor_width (1.0)
160         , _step_edit_cursor_position (0.0)
161         , _channel_selection_scoped_note (0)
162         , _temporary_note_group (0)
163         , _mouse_state(None)
164         , _pressed_button(0)
165         , _sort_needed (true)
166         , _optimization_iterator (_events.end())
167         , _list_editor (0)
168         , _no_sound_notes (false)
169         , _last_display_zoom (0)
170         , _last_event_x (0)
171         , _last_event_y (0)
172         , _grabbed_keyboard (false)
173         , _entered (false)
174         , _mouse_changed_selection (false)
175 {
176         CANVAS_DEBUG_NAME (_note_group, string_compose ("note group for %1", get_item_name()));
177         _note_group->raise_to_top();
178
179         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
180
181         connect_to_diskstream ();
182
183         SelectionCleared.connect (_selection_cleared_connection, invalidator (*this), boost::bind (&MidiRegionView::selection_cleared, this, _1), gui_context ());
184
185         PublicEditor& editor (trackview.editor());
186         editor.get_selection().ClearMidiNoteSelection.connect (_clear_midi_selection_connection, invalidator (*this), boost::bind (&MidiRegionView::clear_midi_selection, this), gui_context ());
187 }
188
189 void
190 MidiRegionView::parameter_changed (std::string const & p)
191 {
192         if (p == "display-first-midi-bank-as-zero") {
193                 if (_enable_display) {
194                         redisplay_model();
195                 }
196         }
197 }
198
199 MidiRegionView::MidiRegionView (const MidiRegionView& other)
200         : sigc::trackable(other)
201         , RegionView (other)
202         , _current_range_min(0)
203         , _current_range_max(0)
204         , _region_relative_time_converter(other.region_relative_time_converter())
205         , _source_relative_time_converter(other.source_relative_time_converter())
206         , _active_notes(0)
207         , _note_group (new ArdourCanvas::Container (get_canvas_group()))
208         , _note_diff_command (0)
209         , _ghost_note(0)
210         , _step_edit_cursor (0)
211         , _step_edit_cursor_width (1.0)
212         , _step_edit_cursor_position (0.0)
213         , _channel_selection_scoped_note (0)
214         , _temporary_note_group (0)
215         , _mouse_state(None)
216         , _pressed_button(0)
217         , _sort_needed (true)
218         , _optimization_iterator (_events.end())
219         , _list_editor (0)
220         , _no_sound_notes (false)
221         , _last_display_zoom (0)
222         , _last_event_x (0)
223         , _last_event_y (0)
224         , _grabbed_keyboard (false)
225         , _entered (false)
226         , _mouse_changed_selection (false)
227 {
228         init (false);
229 }
230
231 MidiRegionView::MidiRegionView (const MidiRegionView& other, boost::shared_ptr<MidiRegion> region)
232         : RegionView (other, boost::shared_ptr<Region> (region))
233         , _current_range_min(0)
234         , _current_range_max(0)
235         , _region_relative_time_converter(other.region_relative_time_converter())
236         , _source_relative_time_converter(other.source_relative_time_converter())
237         , _active_notes(0)
238         , _note_group (new ArdourCanvas::Container (get_canvas_group()))
239         , _note_diff_command (0)
240         , _ghost_note(0)
241         , _step_edit_cursor (0)
242         , _step_edit_cursor_width (1.0)
243         , _step_edit_cursor_position (0.0)
244         , _channel_selection_scoped_note (0)
245         , _temporary_note_group (0)
246         , _mouse_state(None)
247         , _pressed_button(0)
248         , _sort_needed (true)
249         , _optimization_iterator (_events.end())
250         , _list_editor (0)
251         , _no_sound_notes (false)
252         , _last_display_zoom (0)
253         , _last_event_x (0)
254         , _last_event_y (0)
255         , _grabbed_keyboard (false)
256         , _entered (false)
257         , _mouse_changed_selection (false)
258 {
259         init (true);
260 }
261
262 void
263 MidiRegionView::init (bool wfd)
264 {
265         PublicEditor::DropDownKeys.connect (sigc::mem_fun (*this, &MidiRegionView::drop_down_keys));
266
267         NoteBase::NoteBaseDeleted.connect (note_delete_connection, MISSING_INVALIDATOR,
268                                            boost::bind (&MidiRegionView::maybe_remove_deleted_note_from_selection, this, _1),
269                                            gui_context());
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         trackview.editor().verbose_cursor()->hide ();
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                 editor.verbose_cursor()->hide ();
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                                 editor.verbose_cursor()->hide ();
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         trackview.editor().verbose_cursor()->hide ();
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 (!ARDOUR_UI::config()->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         trackview.editor().verbose_cursor()->hide ();
1382
1383         note_delete_connection.disconnect ();
1384
1385         delete _list_editor;
1386
1387         RegionViewGoingAway (this); /* EMIT_SIGNAL */
1388
1389         if (_active_notes) {
1390                 end_write();
1391         }
1392
1393         _selection_cleared_connection.disconnect ();
1394
1395         _selection.clear();
1396         clear_events (false);
1397
1398         delete _note_group;
1399         delete _note_diff_command;
1400         delete _step_edit_cursor;
1401         delete _temporary_note_group;
1402 }
1403
1404 void
1405 MidiRegionView::region_resized (const PropertyChange& what_changed)
1406 {
1407         RegionView::region_resized(what_changed);
1408
1409         if (what_changed.contains (ARDOUR::Properties::position)) {
1410                 _region_relative_time_converter.set_origin_b(_region->position());
1411                 set_duration(_region->length(), 0);
1412                 if (_enable_display) {
1413                         redisplay_model();
1414                 }
1415         }
1416
1417         if (what_changed.contains (ARDOUR::Properties::start) ||
1418             what_changed.contains (ARDOUR::Properties::position)) {
1419                 _source_relative_time_converter.set_origin_b (_region->position() - _region->start());
1420         }
1421 }
1422
1423 void
1424 MidiRegionView::reset_width_dependent_items (double pixel_width)
1425 {
1426         RegionView::reset_width_dependent_items(pixel_width);
1427
1428         if (_enable_display) {
1429                 redisplay_model();
1430         }
1431
1432         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1433                 if ((*x)->canvas_item()->width() >= _pixel_width) {
1434                         (*x)->hide();
1435                 } else {
1436                         (*x)->show();
1437                 }
1438         }
1439
1440         move_step_edit_cursor (_step_edit_cursor_position);
1441         set_step_edit_cursor_width (_step_edit_cursor_width);
1442 }
1443
1444 void
1445 MidiRegionView::set_height (double height)
1446 {
1447         double old_height = _height;
1448         RegionView::set_height(height);
1449
1450         apply_note_range (midi_stream_view()->lowest_note(),
1451                           midi_stream_view()->highest_note(),
1452                           height != old_height);
1453
1454         if (name_text) {
1455                 name_text->raise_to_top();
1456         }
1457
1458         for (PatchChanges::iterator x = _patch_changes.begin(); x != _patch_changes.end(); ++x) {
1459                 (*x)->set_height (midi_stream_view()->contents_height());
1460         }
1461
1462         if (_step_edit_cursor) {
1463                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
1464         }
1465 }
1466
1467
1468 /** Apply the current note range from the stream view
1469  * by repositioning/hiding notes as necessary
1470  */
1471 void
1472 MidiRegionView::apply_note_range (uint8_t min, uint8_t max, bool force)
1473 {
1474         if (!_enable_display) {
1475                 return;
1476         }
1477
1478         if (!force && _current_range_min == min && _current_range_max == max) {
1479                 return;
1480         }
1481
1482         _current_range_min = min;
1483         _current_range_max = max;
1484
1485         for (Events::const_iterator i = _events.begin(); i != _events.end(); ++i) {
1486                 NoteBase* event = *i;
1487                 boost::shared_ptr<NoteType> note (event->note());
1488
1489                 if (note->note() < _current_range_min ||
1490                     note->note() > _current_range_max) {
1491                         event->hide();
1492                 } else {
1493                         event->show();
1494                 }
1495
1496                 if (Note* cnote = dynamic_cast<Note*>(event)) {
1497
1498                         const double y0 = 1. + floor (midi_stream_view()->note_to_y(note->note()));
1499                         const double y1 = y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1.);
1500
1501                         if (y0 < 0 || y1 >= _height) {
1502                                 /* During DnD, the region uses the 'old/current'
1503                                  * midi_stream_view()'s range and its position/height calculation.
1504                                  *
1505                                  * Ideally DnD would decouple the midi_stream_view() for the
1506                                  * region(s) being dragged and set it to the target's range
1507                                  * (or in case of the drop-zone, FullRange).
1508                                  * but I don't see how this can be done without major rework.
1509                                  *
1510                                  * For now, just prevent visual bleeding of events in case
1511                                  * the target-track is smaller.
1512                                  */
1513                                 event->hide();
1514                                 continue;
1515                         }
1516                         cnote->set_y0 (y0);
1517                         cnote->set_y1 (y1);
1518
1519                 } else if (Hit* chit = dynamic_cast<Hit*>(event)) {
1520                         update_hit (chit);
1521                 }
1522         }
1523 }
1524
1525 GhostRegion*
1526 MidiRegionView::add_ghost (TimeAxisView& tv)
1527 {
1528         double unit_position = _region->position () / samples_per_pixel;
1529         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*>(&tv);
1530         MidiGhostRegion* ghost;
1531
1532         if (mtv && mtv->midi_view()) {
1533                 /* if ghost is inserted into midi track, use a dedicated midi ghost canvas group
1534                    to allow having midi notes on top of note lines and waveforms.
1535                 */
1536                 ghost = new MidiGhostRegion (*mtv->midi_view(), trackview, unit_position);
1537         } else {
1538                 ghost = new MidiGhostRegion (tv, trackview, unit_position);
1539         }
1540
1541         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
1542                 ghost->add_note(*i);
1543         }
1544
1545         ghost->set_height ();
1546         ghost->set_duration (_region->length() / samples_per_pixel);
1547         ghosts.push_back (ghost);
1548
1549         GhostRegion::CatchDeletion.connect (*this, invalidator (*this), boost::bind (&RegionView::remove_ghost, this, _1), gui_context());
1550
1551         return ghost;
1552 }
1553
1554
1555 /** Begin tracking note state for successive calls to add_event
1556  */
1557 void
1558 MidiRegionView::begin_write()
1559 {
1560         if (_active_notes) {
1561                 delete[] _active_notes;
1562         }
1563         _active_notes = new Note*[128];
1564         for (unsigned i = 0; i < 128; ++i) {
1565                 _active_notes[i] = 0;
1566         }
1567 }
1568
1569
1570 /** Destroy note state for add_event
1571  */
1572 void
1573 MidiRegionView::end_write()
1574 {
1575         delete[] _active_notes;
1576         _active_notes = 0;
1577         _marked_for_selection.clear();
1578         _marked_for_velocity.clear();
1579 }
1580
1581
1582 /** Resolve an active MIDI note (while recording).
1583  */
1584 void
1585 MidiRegionView::resolve_note(uint8_t note, Evoral::Beats end_time)
1586 {
1587         if (midi_view()->note_mode() != Sustained) {
1588                 return;
1589         }
1590
1591         if (_active_notes && _active_notes[note]) {
1592                 /* Set note length so update_note() works.  Note this is a local note
1593                    for recording, not from a model, so we can safely mess with it. */
1594                 _active_notes[note]->note()->set_length(
1595                         end_time - _active_notes[note]->note()->time());
1596
1597                 /* End time is relative to the region being recorded. */
1598                 const framepos_t end_time_frames = region_beats_to_region_frames(end_time);
1599
1600                 _active_notes[note]->set_x1 (trackview.editor().sample_to_pixel(end_time_frames));
1601                 _active_notes[note]->set_outline_all ();
1602                 _active_notes[note] = 0;
1603         }
1604 }
1605
1606
1607 /** Extend active notes to rightmost edge of region (if length is changed)
1608  */
1609 void
1610 MidiRegionView::extend_active_notes()
1611 {
1612         if (!_active_notes) {
1613                 return;
1614         }
1615
1616         for (unsigned i = 0; i < 128; ++i) {
1617                 if (_active_notes[i]) {
1618                         _active_notes[i]->set_x1(
1619                                 trackview.editor().sample_to_pixel(_region->length()));
1620                 }
1621         }
1622 }
1623
1624 void
1625 MidiRegionView::play_midi_note(boost::shared_ptr<NoteType> note)
1626 {
1627         if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1628                 return;
1629         }
1630
1631         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1632
1633         if (!route_ui || !route_ui->midi_track()) {
1634                 return;
1635         }
1636
1637         NotePlayer* np = new NotePlayer (route_ui->midi_track ());
1638         np->add (note);
1639         np->play ();
1640
1641         /* NotePlayer deletes itself */
1642 }
1643
1644 void
1645 MidiRegionView::start_playing_midi_note(boost::shared_ptr<NoteType> note)
1646 {
1647         const std::vector< boost::shared_ptr<NoteType> > notes(1, note);
1648         start_playing_midi_chord(notes);
1649 }
1650
1651 void
1652 MidiRegionView::start_playing_midi_chord (vector<boost::shared_ptr<NoteType> > notes)
1653 {
1654         if (_no_sound_notes || !ARDOUR_UI::config()->get_sound_midi_notes()) {
1655                 return;
1656         }
1657
1658         RouteUI* route_ui = dynamic_cast<RouteUI*> (&trackview);
1659
1660         if (!route_ui || !route_ui->midi_track()) {
1661                 return;
1662         }
1663
1664         NotePlayer* player = new NotePlayer (route_ui->midi_track());
1665
1666         for (vector<boost::shared_ptr<NoteType> >::iterator n = notes.begin(); n != notes.end(); ++n) {
1667                 player->add (*n);
1668         }
1669
1670         player->play ();
1671 }
1672
1673
1674 bool
1675 MidiRegionView::note_in_region_range (const boost::shared_ptr<NoteType> note, bool& visible) const
1676 {
1677         /* This is imprecise due to all the conversion conversion involved, so only
1678            hide notes if they seem to start more than one tick before the start. */
1679         const framecnt_t tick_frames       = Evoral::Beats::tick().to_ticks(trackview.session()->frame_rate());
1680         const framepos_t note_start_frames = source_beats_to_region_frames (note->time());
1681         const bool       outside           = ((note_start_frames <= -tick_frames) ||
1682                                               (note_start_frames >= _region->length()));
1683
1684         visible = (note->note() >= midi_stream_view()->lowest_note()) &&
1685                 (note->note() <= midi_stream_view()->highest_note());
1686
1687         return !outside;
1688 }
1689
1690 void
1691 MidiRegionView::update_note (NoteBase* note, bool update_ghost_regions)
1692 {
1693         Note* sus = NULL;
1694         Hit*  hit = NULL;
1695         if ((sus = dynamic_cast<Note*>(note))) {
1696                 update_sustained(sus, update_ghost_regions);
1697         } else if ((hit = dynamic_cast<Hit*>(note))) {
1698                 update_hit(hit, update_ghost_regions);
1699         }
1700 }
1701
1702 /** Update a canvas note's size from its model note.
1703  *  @param ev Canvas note to update.
1704  *  @param update_ghost_regions true to update the note in any ghost regions that we have, otherwise false.
1705  */
1706 void
1707 MidiRegionView::update_sustained (Note* ev, bool update_ghost_regions)
1708 {
1709         boost::shared_ptr<NoteType> note = ev->note();
1710         const double x = trackview.editor().sample_to_pixel (source_beats_to_region_frames (note->time()));
1711         const double y0 = 1 + floor(midi_stream_view()->note_to_y(note->note()));
1712
1713         ev->set_x0 (x);
1714         ev->set_y0 (y0);
1715
1716         /* trim note display to not overlap the end of its region */
1717
1718         if (note->length() > 0) {
1719                 const framepos_t note_end_frames = min (source_beats_to_region_frames (note->end_time()), _region->length());
1720                 ev->set_x1 (trackview.editor().sample_to_pixel (note_end_frames));
1721         } else {
1722                 ev->set_x1 (trackview.editor().sample_to_pixel (_region->length()));
1723         }
1724
1725         ev->set_y1 (y0 + std::max(1., floor(midi_stream_view()->note_height()) - 1));
1726
1727         if (!note->length()) {
1728                 if (_active_notes && note->note() < 128) {
1729                         Note* const old_rect = _active_notes[note->note()];
1730                         if (old_rect) {
1731                                 /* There is an active note on this key, so we have a stuck
1732                                    note.  Finish the old rectangle here. */
1733                                 old_rect->set_x1 (x);
1734                                 old_rect->set_outline_all ();
1735                         }
1736                         _active_notes[note->note()] = ev;
1737                 }
1738                 /* outline all but right edge */
1739                 ev->set_outline_what (ArdourCanvas::Rectangle::What (
1740                                               ArdourCanvas::Rectangle::TOP|
1741                                               ArdourCanvas::Rectangle::LEFT|
1742                                               ArdourCanvas::Rectangle::BOTTOM));
1743         } else {
1744                 /* outline all edges */
1745                 ev->set_outline_all ();
1746         }
1747
1748         // Update color in case velocity has changed
1749         ev->set_fill_color(ev->base_color());
1750         ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1751
1752         if (update_ghost_regions) {
1753                 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1754                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1755                         if (gr) {
1756                                 gr->update_note (ev);
1757                         }
1758                 }
1759         }
1760 }
1761
1762 void
1763 MidiRegionView::update_hit (Hit* ev, bool update_ghost_regions)
1764 {
1765         boost::shared_ptr<NoteType> note = ev->note();
1766
1767         const framepos_t note_start_frames = source_beats_to_region_frames(note->time());
1768         const double x = trackview.editor().sample_to_pixel(note_start_frames);
1769         const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1770         const double y = 1.5 + floor(midi_stream_view()->note_to_y(note->note())) + diamond_size * .5;
1771
1772         // see DnD note in MidiRegionView::apply_note_range() above
1773         if (y <= 0 || y >= _height) {
1774                 ev->hide();
1775         } else {
1776                 ev->show();
1777         }
1778
1779         ev->set_position (ArdourCanvas::Duple (x, y));
1780         ev->set_height (diamond_size);
1781
1782         // Update color in case velocity has changed
1783         ev->set_fill_color(ev->base_color());
1784         ev->set_outline_color(ev->calculate_outline(ev->base_color(), ev->selected()));
1785
1786         if (update_ghost_regions) {
1787                 for (std::vector<GhostRegion*>::iterator i = ghosts.begin(); i != ghosts.end(); ++i) {
1788                         MidiGhostRegion* gr = dynamic_cast<MidiGhostRegion*> (*i);
1789                         if (gr) {
1790                                 gr->update_note (ev);
1791                         }
1792                 }
1793         }
1794 }
1795
1796 /** Add a MIDI note to the view (with length).
1797  *
1798  * If in sustained mode, notes with length 0 will be considered active
1799  * notes, and resolve_note should be called when the corresponding note off
1800  * event arrives, to properly display the note.
1801  */
1802 NoteBase*
1803 MidiRegionView::add_note(const boost::shared_ptr<NoteType> note, bool visible)
1804 {
1805         NoteBase* event = 0;
1806
1807         if (midi_view()->note_mode() == Sustained) {
1808
1809                 Note* ev_rect = new Note (*this, _note_group, note);
1810
1811                 update_sustained (ev_rect);
1812
1813                 event = ev_rect;
1814
1815         } else if (midi_view()->note_mode() == Percussive) {
1816
1817                 const double diamond_size = std::max(1., floor(midi_stream_view()->note_height()) - 2.);
1818
1819                 Hit* ev_diamond = new Hit (*this, _note_group, diamond_size, note);
1820
1821                 update_hit (ev_diamond);
1822
1823                 event = ev_diamond;
1824
1825         } else {
1826                 event = 0;
1827         }
1828
1829         if (event) {
1830                 MidiGhostRegion* gr;
1831
1832                 for (std::vector<GhostRegion*>::iterator g = ghosts.begin(); g != ghosts.end(); ++g) {
1833                         if ((gr = dynamic_cast<MidiGhostRegion*>(*g)) != 0) {
1834                                 gr->add_note(event);
1835                         }
1836                 }
1837
1838                 if (_marked_for_selection.find(note) != _marked_for_selection.end()) {
1839                         note_selected(event, true);
1840                 }
1841
1842                 if (_marked_for_velocity.find(note) != _marked_for_velocity.end()) {
1843                         event->show_velocity();
1844                 }
1845
1846                 event->on_channel_selection_change (get_selected_channels());
1847                 _events.push_back(event);
1848
1849                 if (visible) {
1850                         event->show();
1851                 } else {
1852                         event->hide ();
1853                 }
1854         }
1855
1856         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1857         MidiStreamView* const view = mtv->midi_view();
1858
1859         view->update_note_range (note->note());
1860         return event;
1861 }
1862
1863 void
1864 MidiRegionView::step_add_note (uint8_t channel, uint8_t number, uint8_t velocity,
1865                                Evoral::Beats pos, Evoral::Beats len)
1866 {
1867         boost::shared_ptr<NoteType> new_note (new NoteType (channel, pos, len, number, velocity));
1868
1869         /* potentially extend region to hold new note */
1870
1871         framepos_t end_frame = source_beats_to_absolute_frames (new_note->end_time());
1872         framepos_t region_end = _region->last_frame();
1873
1874         if (end_frame > region_end) {
1875                 _region->set_length (end_frame - _region->position());
1876         }
1877
1878         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
1879         MidiStreamView* const view = mtv->midi_view();
1880
1881         view->update_note_range(new_note->note());
1882
1883         _marked_for_selection.clear ();
1884
1885         start_note_diff_command (_("step add"));
1886
1887         clear_selection ();
1888         note_diff_add_note (new_note, true, false);
1889
1890         apply_diff();
1891
1892         // last_step_edit_note = new_note;
1893 }
1894
1895 void
1896 MidiRegionView::step_sustain (Evoral::Beats beats)
1897 {
1898         change_note_lengths (false, false, beats, false, true);
1899 }
1900
1901 /** Add a new patch change flag to the canvas.
1902  * @param patch the patch change to add
1903  * @param the text to display in the flag
1904  * @param active_channel true to display the flag as on an active channel, false to grey it out for an inactive channel.
1905  */
1906 void
1907 MidiRegionView::add_canvas_patch_change (MidiModel::PatchChangePtr patch, const string& displaytext, bool /*active_channel*/)
1908 {
1909         framecnt_t region_frames = source_beats_to_region_frames (patch->time());
1910         const double x = trackview.editor().sample_to_pixel (region_frames);
1911
1912         double const height = midi_stream_view()->contents_height();
1913
1914         // CAIROCANVAS: active_channel info removed from PatcChange constructor
1915         // so we need to do something more sophisticated to keep its color
1916         // appearance (MidiPatchChangeFill/MidiPatchChangeInactiveChannelFill)
1917         // up to date.
1918
1919         boost::shared_ptr<PatchChange> patch_change = boost::shared_ptr<PatchChange>(
1920                 new PatchChange(*this, group,
1921                                 displaytext,
1922                                 height,
1923                                 x, 1.0,
1924                                 instrument_info(),
1925                                 patch));
1926
1927         if (patch_change->item().width() < _pixel_width) {
1928                 // Show unless patch change is beyond the region bounds
1929                 if (region_frames < 0 || region_frames >= _region->length()) {
1930                         patch_change->hide();
1931                 } else {
1932                         patch_change->show();
1933                 }
1934         } else {
1935                 patch_change->hide ();
1936         }
1937
1938         _patch_changes.push_back (patch_change);
1939 }
1940
1941 MIDI::Name::PatchPrimaryKey
1942 MidiRegionView::patch_change_to_patch_key (MidiModel::PatchChangePtr p)
1943 {
1944         return MIDI::Name::PatchPrimaryKey (p->program(), p->bank());
1945 }
1946
1947 /// Return true iff @p pc applies to the given time on the given channel.
1948 static bool
1949 patch_applies (const ARDOUR::MidiModel::constPatchChangePtr pc, Evoral::Beats time, uint8_t channel)
1950 {
1951         return pc->time() <= time && pc->channel() == channel;
1952 }
1953         
1954 void 
1955 MidiRegionView::get_patch_key_at (Evoral::Beats time, uint8_t channel, MIDI::Name::PatchPrimaryKey& key) const
1956 {
1957         // The earliest event not before time
1958         MidiModel::PatchChanges::iterator i = _model->patch_change_lower_bound (time);
1959
1960         // Go backwards until we find the latest PC for this channel, or the start
1961         while (i != _model->patch_changes().begin() &&
1962                (i == _model->patch_changes().end() ||
1963                 !patch_applies(*i, time, channel))) {
1964                 --i;
1965         }
1966
1967         if (i != _model->patch_changes().end() && patch_applies(*i, time, channel)) {
1968                 key.set_bank((*i)->bank());
1969                 key.set_program((*i)->program ());
1970         } else {
1971                 key.set_bank(0);
1972                 key.set_program(0);
1973         }
1974 }
1975
1976 void
1977 MidiRegionView::change_patch_change (PatchChange& pc, const MIDI::Name::PatchPrimaryKey& new_patch)
1978 {
1979         string name = _("alter patch change");
1980         trackview.editor().begin_reversible_command (name);
1981         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
1982
1983         if (pc.patch()->program() != new_patch.program()) {
1984                 c->change_program (pc.patch (), new_patch.program());
1985         }
1986
1987         int const new_bank = new_patch.bank();
1988         if (pc.patch()->bank() != new_bank) {
1989                 c->change_bank (pc.patch (), new_bank);
1990         }
1991
1992         _model->apply_command (*trackview.session(), c);
1993         trackview.editor().commit_reversible_command ();
1994
1995         _patch_changes.clear ();
1996         display_patch_changes ();
1997 }
1998
1999 void
2000 MidiRegionView::change_patch_change (MidiModel::PatchChangePtr old_change, const Evoral::PatchChange<Evoral::Beats> & new_change)
2001 {
2002         string name = _("alter patch change");
2003         trackview.editor().begin_reversible_command (name);
2004         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2005
2006         if (old_change->time() != new_change.time()) {
2007                 c->change_time (old_change, new_change.time());
2008         }
2009
2010         if (old_change->channel() != new_change.channel()) {
2011                 c->change_channel (old_change, new_change.channel());
2012         }
2013
2014         if (old_change->program() != new_change.program()) {
2015                 c->change_program (old_change, new_change.program());
2016         }
2017
2018         if (old_change->bank() != new_change.bank()) {
2019                 c->change_bank (old_change, new_change.bank());
2020         }
2021
2022         _model->apply_command (*trackview.session(), c);
2023         trackview.editor().commit_reversible_command ();
2024
2025         _patch_changes.clear ();
2026         display_patch_changes ();
2027 }
2028
2029 /** Add a patch change to the region.
2030  *  @param t Time in frames relative to region position
2031  *  @param patch Patch to add; time and channel are ignored (time is converted from t, and channel comes from
2032  *  MidiTimeAxisView::get_channel_for_add())
2033  */
2034 void
2035 MidiRegionView::add_patch_change (framecnt_t t, Evoral::PatchChange<Evoral::Beats> const & patch)
2036 {
2037         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
2038         string name = _("add patch change");
2039
2040         trackview.editor().begin_reversible_command (name);
2041         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (name);
2042         c->add (MidiModel::PatchChangePtr (
2043                         new Evoral::PatchChange<Evoral::Beats> (
2044                                 absolute_frames_to_source_beats (_region->position() + t),
2045                                 mtv->get_channel_for_add(), patch.program(), patch.bank()
2046                                 )
2047                         )
2048                 );
2049
2050         _model->apply_command (*trackview.session(), c);
2051         trackview.editor().commit_reversible_command ();
2052
2053         _patch_changes.clear ();
2054         display_patch_changes ();
2055 }
2056
2057 void
2058 MidiRegionView::move_patch_change (PatchChange& pc, Evoral::Beats t)
2059 {
2060         trackview.editor().begin_reversible_command (_("move patch change"));
2061         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("move patch change"));
2062         c->change_time (pc.patch (), t);
2063         _model->apply_command (*trackview.session(), c);
2064         trackview.editor().commit_reversible_command ();
2065
2066         _patch_changes.clear ();
2067         display_patch_changes ();
2068 }
2069
2070 void
2071 MidiRegionView::delete_patch_change (PatchChange* pc)
2072 {
2073         trackview.editor().begin_reversible_command (_("delete patch change"));
2074         MidiModel::PatchChangeDiffCommand* c = _model->new_patch_change_diff_command (_("delete patch change"));
2075         c->remove (pc->patch ());
2076         _model->apply_command (*trackview.session(), c);
2077         trackview.editor().commit_reversible_command ();
2078
2079         _patch_changes.clear ();
2080         display_patch_changes ();
2081 }
2082
2083 void
2084 MidiRegionView::step_patch (PatchChange& patch, bool bank, int delta)
2085 {
2086         MIDI::Name::PatchPrimaryKey key = patch_change_to_patch_key(patch.patch());
2087         if (bank) {
2088                 key.set_bank(key.bank() + delta);
2089         } else {
2090                 key.set_program(key.program() + delta);
2091         }
2092         change_patch_change(patch, key);
2093 }
2094
2095 void
2096 MidiRegionView::maybe_remove_deleted_note_from_selection (NoteBase* cne)
2097 {
2098         if (_selection.empty()) {
2099                 return;
2100         }
2101
2102         _selection.erase (cne);
2103 }
2104
2105 void
2106 MidiRegionView::delete_selection()
2107 {
2108         if (_selection.empty()) {
2109                 return;
2110         }
2111
2112         start_note_diff_command (_("delete selection"));
2113
2114         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2115                 if ((*i)->selected()) {
2116                         _note_diff_command->remove((*i)->note());
2117                 }
2118         }
2119
2120         _selection.clear();
2121
2122         apply_diff ();
2123 }
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         trackview.editor().verbose_cursor()->hide ();
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 && ARDOUR_UI::config()->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  *  @return Snapped frame relative to the region position.
2623  */
2624 framepos_t
2625 MidiRegionView::snap_pixel_to_sample(double x)
2626 {
2627         PublicEditor& editor (trackview.editor());
2628         return snap_frame_to_frame (editor.pixel_to_sample (x));
2629 }
2630
2631 /** @param x Pixel relative to the region position.
2632  *  @return Snapped pixel relative to the region position.
2633  */
2634 double
2635 MidiRegionView::snap_to_pixel(double x)
2636 {
2637         return (double) trackview.editor().sample_to_pixel(snap_pixel_to_sample(x));
2638 }
2639
2640 double
2641 MidiRegionView::get_position_pixels()
2642 {
2643         framepos_t region_frame = get_position();
2644         return trackview.editor().sample_to_pixel(region_frame);
2645 }
2646
2647 double
2648 MidiRegionView::get_end_position_pixels()
2649 {
2650         framepos_t frame = get_position() + get_duration ();
2651         return trackview.editor().sample_to_pixel(frame);
2652 }
2653
2654 framepos_t
2655 MidiRegionView::source_beats_to_absolute_frames(Evoral::Beats beats) const
2656 {
2657         /* the time converter will return the frame corresponding to `beats'
2658            relative to the start of the source. The start of the source
2659            is an implied position given by region->position - region->start
2660         */
2661         const framepos_t source_start = _region->position() - _region->start();
2662         return  source_start +  _source_relative_time_converter.to (beats);
2663 }
2664
2665 Evoral::Beats
2666 MidiRegionView::absolute_frames_to_source_beats(framepos_t frames) const
2667 {
2668         /* the `frames' argument needs to be converted into a frame count
2669            relative to the start of the source before being passed in to the
2670            converter.
2671         */
2672         const framepos_t source_start = _region->position() - _region->start();
2673         return  _source_relative_time_converter.from (frames - source_start);
2674 }
2675
2676 framepos_t
2677 MidiRegionView::region_beats_to_region_frames(Evoral::Beats beats) const
2678 {
2679         return _region_relative_time_converter.to(beats);
2680 }
2681
2682 Evoral::Beats
2683 MidiRegionView::region_frames_to_region_beats(framepos_t frames) const
2684 {
2685         return _region_relative_time_converter.from(frames);
2686 }
2687
2688 void
2689 MidiRegionView::begin_resizing (bool /*at_front*/)
2690 {
2691         _resize_data.clear();
2692
2693         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
2694                 Note *note = dynamic_cast<Note*> (*i);
2695
2696                 // only insert CanvasNotes into the map
2697                 if (note) {
2698                         NoteResizeData *resize_data = new NoteResizeData();
2699                         resize_data->note = note;
2700
2701                         // create a new SimpleRect from the note which will be the resize preview
2702                         ArdourCanvas::Rectangle *resize_rect = new ArdourCanvas::Rectangle (_note_group, 
2703                                                                                             ArdourCanvas::Rect (note->x0(), note->y0(), note->x0(), note->y1()));
2704
2705                         // calculate the colors: get the color settings
2706                         uint32_t fill_color = UINT_RGBA_CHANGE_A(
2707                                 ARDOUR_UI::config()->color ("midi note selected"),
2708                                 128);
2709
2710                         // make the resize preview notes more transparent and bright
2711                         fill_color = UINT_INTERPOLATE(fill_color, 0xFFFFFF40, 0.5);
2712
2713                         // calculate color based on note velocity
2714                         resize_rect->set_fill_color (UINT_INTERPOLATE(
2715                                 NoteBase::meter_style_fill_color(note->note()->velocity(), note->selected()),
2716                                 fill_color,
2717                                 0.85));
2718
2719                         resize_rect->set_outline_color (NoteBase::calculate_outline (
2720                                                                 ARDOUR_UI::config()->color ("midi note selected")));
2721
2722                         resize_data->resize_rect = resize_rect;
2723                         _resize_data.push_back(resize_data);
2724                 }
2725         }
2726 }
2727
2728 /** Update resizing notes while user drags.
2729  * @param primary `primary' note for the drag; ie the one that is used as the reference in non-relative mode.
2730  * @param at_front which end of the note (true == note on, false == note off)
2731  * @param delta_x change in mouse position since the start of the drag
2732  * @param relative true if relative resizing is taking place, false if absolute resizing.  This only makes
2733  * a difference when multiple notes are being resized; in relative mode, each note's length is changed by the
2734  * amount of the drag.  In non-relative mode, all selected notes are set to have the same start or end point
2735  * as the \a primary note.
2736  */
2737 void
2738 MidiRegionView::update_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2739 {
2740         bool cursor_set = false;
2741
2742         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2743                 ArdourCanvas::Rectangle* resize_rect = (*i)->resize_rect;
2744                 Note* canvas_note = (*i)->note;
2745                 double current_x;
2746
2747                 if (at_front) {
2748                         if (relative) {
2749                                 current_x = canvas_note->x0() + delta_x;
2750                         } else {
2751                                 current_x = primary->x0() + delta_x;
2752                         }
2753                 } else {
2754                         if (relative) {
2755                                 current_x = canvas_note->x1() + delta_x;
2756                         } else {
2757                                 current_x = primary->x1() + delta_x;
2758                         }
2759                 }
2760
2761                 if (current_x < 0) {
2762                         // This works even with snapping because RegionView::snap_frame_to_frame()
2763                         // snaps forward if the snapped sample is before the beginning of the region
2764                         current_x = 0;
2765                 }
2766                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2767                         current_x = trackview.editor().sample_to_pixel(_region->length());
2768                 }
2769
2770                 if (at_front) {
2771                         resize_rect->set_x0 (snap_to_pixel(current_x));
2772                         resize_rect->set_x1 (canvas_note->x1());
2773                 } else {
2774                         resize_rect->set_x1 (snap_to_pixel(current_x));
2775                         resize_rect->set_x0 (canvas_note->x0());
2776                 }
2777
2778                 if (!cursor_set) {
2779                         const double  snapped_x = snap_pixel_to_sample (current_x);
2780                         Evoral::Beats beats     = region_frames_to_region_beats (snapped_x);
2781                         Evoral::Beats len       = Evoral::Beats();
2782
2783                         if (at_front) {
2784                                 if (beats < canvas_note->note()->end_time()) {
2785                                         len = canvas_note->note()->time() - beats;
2786                                         len += canvas_note->note()->length();
2787                                 }
2788                         } else {
2789                                 if (beats >= canvas_note->note()->time()) {
2790                                         len = beats - canvas_note->note()->time();
2791                                 }
2792                         }
2793
2794                         len = std::max(Evoral::Beats(1 / 512.0), len);
2795
2796                         char buf[16];
2797                         snprintf (buf, sizeof (buf), "%.3g beats", len.to_double());
2798                         show_verbose_cursor (buf, 0, 0);
2799
2800                         cursor_set = true;
2801                 }
2802
2803         }
2804 }
2805
2806
2807 /** Finish resizing notes when the user releases the mouse button.
2808  *  Parameters the same as for \a update_resizing().
2809  */
2810 void
2811 MidiRegionView::commit_resizing (NoteBase* primary, bool at_front, double delta_x, bool relative)
2812 {
2813         _note_diff_command = _model->new_note_diff_command (_("resize notes"));
2814
2815         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2816                 Note*  canvas_note = (*i)->note;
2817                 ArdourCanvas::Rectangle*  resize_rect = (*i)->resize_rect;
2818
2819                 /* Get the new x position for this resize, which is in pixels relative
2820                  * to the region position.
2821                  */
2822                 
2823                 double current_x;
2824
2825                 if (at_front) {
2826                         if (relative) {
2827                                 current_x = canvas_note->x0() + delta_x;
2828                         } else {
2829                                 current_x = primary->x0() + delta_x;
2830                         }
2831                 } else {
2832                         if (relative) {
2833                                 current_x = canvas_note->x1() + delta_x;
2834                         } else {
2835                                 current_x = primary->x1() + delta_x;
2836                         }
2837                 }
2838
2839                 if (current_x < 0) {
2840                         current_x = 0;
2841                 }
2842                 if (current_x > trackview.editor().sample_to_pixel(_region->length())) {
2843                         current_x = trackview.editor().sample_to_pixel(_region->length());
2844                 }
2845
2846                 /* Convert that to a frame within the source */
2847                 current_x = snap_pixel_to_sample (current_x) + _region->start ();
2848
2849                 /* and then to beats */
2850                 const Evoral::Beats x_beats = region_frames_to_region_beats (current_x);
2851
2852                 if (at_front && x_beats < canvas_note->note()->end_time()) {
2853                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::StartTime, x_beats);
2854
2855                         Evoral::Beats len = canvas_note->note()->time() - x_beats;
2856                         len += canvas_note->note()->length();
2857
2858                         if (!!len) {
2859                                 note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2860                         }
2861                 }
2862
2863                 if (!at_front) {
2864                         const Evoral::Beats len = std::max(Evoral::Beats(1 / 512.0),
2865                                                            x_beats - canvas_note->note()->time());
2866                         note_diff_add_change (canvas_note, MidiModel::NoteDiffCommand::Length, len);
2867                 }
2868
2869                 delete resize_rect;
2870                 delete (*i);
2871         }
2872
2873         _resize_data.clear();
2874         apply_diff(true);
2875 }
2876
2877 void
2878 MidiRegionView::abort_resizing ()
2879 {
2880         for (std::vector<NoteResizeData *>::iterator i = _resize_data.begin(); i != _resize_data.end(); ++i) {
2881                 delete (*i)->resize_rect;
2882                 delete *i;
2883         }
2884
2885         _resize_data.clear ();
2886 }
2887
2888 void
2889 MidiRegionView::change_note_velocity(NoteBase* event, int8_t velocity, bool relative)
2890 {
2891         uint8_t new_velocity;
2892
2893         if (relative) {
2894                 new_velocity = event->note()->velocity() + velocity;
2895                 clamp_to_0_127(new_velocity);
2896         } else {
2897                 new_velocity = velocity;
2898         }
2899
2900         event->set_selected (event->selected()); // change color
2901
2902         note_diff_add_change (event, MidiModel::NoteDiffCommand::Velocity, new_velocity);
2903 }
2904
2905 void
2906 MidiRegionView::change_note_note (NoteBase* event, int8_t note, bool relative)
2907 {
2908         uint8_t new_note;
2909
2910         if (relative) {
2911                 new_note = event->note()->note() + note;
2912         } else {
2913                 new_note = note;
2914         }
2915
2916         clamp_to_0_127 (new_note);
2917         note_diff_add_change (event, MidiModel::NoteDiffCommand::NoteNumber, new_note);
2918 }
2919
2920 void
2921 MidiRegionView::trim_note (NoteBase* event, Evoral::Beats front_delta, Evoral::Beats end_delta)
2922 {
2923         bool change_start = false;
2924         bool change_length = false;
2925         Evoral::Beats new_start;
2926         Evoral::Beats new_length;
2927
2928         /* NOTE: the semantics of the two delta arguments are slightly subtle:
2929
2930            front_delta: if positive - move the start of the note later in time (shortening it)
2931            if negative - move the start of the note earlier in time (lengthening it)
2932
2933            end_delta:   if positive - move the end of the note later in time (lengthening it)
2934            if negative - move the end of the note earlier in time (shortening it)
2935         */
2936
2937         if (!!front_delta) {
2938                 if (front_delta < 0) {
2939
2940                         if (event->note()->time() < -front_delta) {
2941                                 new_start = Evoral::Beats();
2942                         } else {
2943                                 new_start = event->note()->time() + front_delta; // moves earlier
2944                         }
2945
2946                         /* start moved toward zero, so move the end point out to where it used to be.
2947                            Note that front_delta is negative, so this increases the length.
2948                         */
2949
2950                         new_length = event->note()->length() - front_delta;
2951                         change_start = true;
2952                         change_length = true;
2953
2954                 } else {
2955
2956                         Evoral::Beats new_pos = event->note()->time() + front_delta;
2957
2958                         if (new_pos < event->note()->end_time()) {
2959                                 new_start = event->note()->time() + front_delta;
2960                                 /* start moved toward the end, so move the end point back to where it used to be */
2961                                 new_length = event->note()->length() - front_delta;
2962                                 change_start = true;
2963                                 change_length = true;
2964                         }
2965                 }
2966
2967         }
2968
2969         if (!!end_delta) {
2970                 bool can_change = true;
2971                 if (end_delta < 0) {
2972                         if (event->note()->length() < -end_delta) {
2973                                 can_change = false;
2974                         }
2975                 }
2976
2977                 if (can_change) {
2978                         new_length = event->note()->length() + end_delta;
2979                         change_length = true;
2980                 }
2981         }
2982
2983         if (change_start) {
2984                 note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_start);
2985         }
2986
2987         if (change_length) {
2988                 note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, new_length);
2989         }
2990 }
2991
2992 void
2993 MidiRegionView::change_note_channel (NoteBase* event, int8_t chn, bool relative)
2994 {
2995         uint8_t new_channel;
2996
2997         if (relative) {
2998                 if (chn < 0.0) {
2999                         if (event->note()->channel() < -chn) {
3000                                 new_channel = 0;
3001                         } else {
3002                                 new_channel = event->note()->channel() + chn;
3003                         }
3004                 } else {
3005                         new_channel = event->note()->channel() + chn;
3006                 }
3007         } else {
3008                 new_channel = (uint8_t) chn;
3009         }
3010
3011         note_diff_add_change (event, MidiModel::NoteDiffCommand::Channel, new_channel);
3012 }
3013
3014 void
3015 MidiRegionView::change_note_time (NoteBase* event, Evoral::Beats delta, bool relative)
3016 {
3017         Evoral::Beats new_time;
3018
3019         if (relative) {
3020                 if (delta < 0.0) {
3021                         if (event->note()->time() < -delta) {
3022                                 new_time = Evoral::Beats();
3023                         } else {
3024                                 new_time = event->note()->time() + delta;
3025                         }
3026                 } else {
3027                         new_time = event->note()->time() + delta;
3028                 }
3029         } else {
3030                 new_time = delta;
3031         }
3032
3033         note_diff_add_change (event, MidiModel::NoteDiffCommand::StartTime, new_time);
3034 }
3035
3036 void
3037 MidiRegionView::change_note_length (NoteBase* event, Evoral::Beats t)
3038 {
3039         note_diff_add_change (event, MidiModel::NoteDiffCommand::Length, t);
3040 }
3041
3042 void
3043 MidiRegionView::change_velocities (bool up, bool fine, bool allow_smush, bool all_together)
3044 {
3045         int8_t delta;
3046         int8_t value = 0;
3047
3048         if (_selection.empty()) {
3049                 return;
3050         }
3051
3052         if (fine) {
3053                 delta = 1;
3054         } else {
3055                 delta = 10;
3056         }
3057
3058         if (!up) {
3059                 delta = -delta;
3060         }
3061
3062         if (!allow_smush) {
3063                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3064                         if ((*i)->note()->velocity() < -delta || (*i)->note()->velocity() + delta > 127) {
3065                                 goto cursor_label;
3066                         }
3067                 }
3068         }
3069
3070         start_note_diff_command (_("change velocities"));
3071
3072         for (Selection::iterator i = _selection.begin(); i != _selection.end();) {
3073                 Selection::iterator next = i;
3074                 ++next;
3075
3076                 if (all_together) {
3077                         if (i == _selection.begin()) {
3078                                 change_note_velocity (*i, delta, true);
3079                                 value = (*i)->note()->velocity() + delta;
3080                         } else {
3081                                 change_note_velocity (*i, value, false);
3082                         }
3083
3084                 } else {
3085                         change_note_velocity (*i, delta, true);
3086                 }
3087
3088                 i = next;
3089         }
3090
3091         apply_diff();
3092
3093   cursor_label:
3094         if (!_selection.empty()) {
3095                 char buf[24];
3096                 snprintf (buf, sizeof (buf), "Vel %d",
3097                           (int) (*_selection.begin())->note()->velocity());
3098                 show_verbose_cursor (buf, 10, 10);
3099         }
3100 }
3101
3102
3103 void
3104 MidiRegionView::transpose (bool up, bool fine, bool allow_smush)
3105 {
3106         if (_selection.empty()) {
3107                 return;
3108         }
3109
3110         int8_t delta;
3111
3112         if (fine) {
3113                 delta = 1;
3114         } else {
3115                 delta = 12;
3116         }
3117
3118         if (!up) {
3119                 delta = -delta;
3120         }
3121
3122         if (!allow_smush) {
3123                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3124                         if (!up) {
3125                                 if ((int8_t) (*i)->note()->note() + delta <= 0) {
3126                                         return;
3127                                 }
3128                         } else {
3129                                 if ((int8_t) (*i)->note()->note() + delta > 127) {
3130                                         return;
3131                                 }
3132                         }
3133                 }
3134         }
3135
3136         start_note_diff_command (_("transpose"));
3137
3138         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3139                 Selection::iterator next = i;
3140                 ++next;
3141                 change_note_note (*i, delta, true);
3142                 i = next;
3143         }
3144
3145         apply_diff ();
3146 }
3147
3148 void
3149 MidiRegionView::change_note_lengths (bool fine, bool shorter, Evoral::Beats delta, bool start, bool end)
3150 {
3151         if (!delta) {
3152                 if (fine) {
3153                         delta = Evoral::Beats(1.0/128.0);
3154                 } else {
3155                         /* grab the current grid distance */
3156                         delta = get_grid_beats(_region->position());
3157                 }
3158         }
3159
3160         if (shorter) {
3161                 delta = -delta;
3162         }
3163
3164         start_note_diff_command (_("change note lengths"));
3165
3166         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3167                 Selection::iterator next = i;
3168                 ++next;
3169
3170                 /* note the negation of the delta for start */
3171
3172                 trim_note (*i,
3173                            (start ? -delta : Evoral::Beats()),
3174                            (end   ? delta  : Evoral::Beats()));
3175                 i = next;
3176         }
3177
3178         apply_diff ();
3179
3180 }
3181
3182 void
3183 MidiRegionView::nudge_notes (bool forward, bool fine)
3184 {
3185         if (_selection.empty()) {
3186                 return;
3187         }
3188
3189         /* pick a note as the point along the timeline to get the nudge distance.
3190            its not necessarily the earliest note, so we may want to pull the notes out
3191            into a vector and sort before using the first one.
3192         */
3193
3194         const framepos_t ref_point = source_beats_to_absolute_frames ((*(_selection.begin()))->note()->time());
3195         Evoral::Beats    delta;
3196
3197         if (!fine) {
3198
3199                 /* non-fine, move by 1 bar regardless of snap */
3200                 delta = Evoral::Beats(trackview.session()->tempo_map().meter_at(ref_point).divisions_per_bar());
3201
3202         } else if (trackview.editor().snap_mode() == Editing::SnapOff) {
3203
3204                 /* grid is off - use nudge distance */
3205
3206                 framepos_t       unused;
3207                 const framecnt_t distance = trackview.editor().get_nudge_distance (ref_point, unused);
3208                 delta = region_frames_to_region_beats (fabs ((double)distance));
3209
3210         } else {
3211
3212                 /* use grid */
3213
3214                 framepos_t next_pos = ref_point;
3215
3216                 if (forward) {
3217                         if (max_framepos - 1 < next_pos) {
3218                                 next_pos += 1;
3219                         }
3220                 } else {
3221                         if (next_pos == 0) {
3222                                 return;
3223                         }
3224                         next_pos -= 1;
3225                 }
3226
3227                 trackview.editor().snap_to (next_pos, (forward ? RoundUpAlways : RoundDownAlways), false);
3228                 const framecnt_t distance = ref_point - next_pos;
3229                 delta = region_frames_to_region_beats (fabs ((double)distance));
3230         }
3231
3232         if (!delta) {
3233                 return;
3234         }
3235
3236         if (!forward) {
3237                 delta = -delta;
3238         }
3239
3240         start_note_diff_command (_("nudge"));
3241
3242         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ) {
3243                 Selection::iterator next = i;
3244                 ++next;
3245                 change_note_time (*i, delta, true);
3246                 i = next;
3247         }
3248
3249         apply_diff ();
3250 }
3251
3252 void
3253 MidiRegionView::change_channel(uint8_t channel)
3254 {
3255         start_note_diff_command(_("change channel"));
3256         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3257                 note_diff_add_change (*i, MidiModel::NoteDiffCommand::Channel, channel);
3258         }
3259
3260         apply_diff();
3261 }
3262
3263
3264 void
3265 MidiRegionView::note_entered(NoteBase* ev)
3266 {
3267         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3268
3269         if (_mouse_state == SelectTouchDragging) {
3270                 note_selected (ev, true);
3271         } else if (editor->current_mouse_mode() == MouseContent) {
3272                 show_verbose_cursor (ev->note ());
3273         } else if (editor->current_mouse_mode() == MouseDraw) {
3274                 show_verbose_cursor (ev->note ());
3275         }
3276 }
3277
3278 void
3279 MidiRegionView::note_left (NoteBase*)
3280 {
3281         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3282
3283         for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3284                 (*i)->hide_velocity ();
3285         }
3286
3287         editor->verbose_cursor()->hide ();
3288 }
3289
3290 void
3291 MidiRegionView::patch_entered (PatchChange* p)
3292 {
3293         ostringstream s;
3294         /* XXX should get patch name if we can */
3295         s << _("Bank ") << (p->patch()->bank() + MIDI_BP_ZERO) << '\n' 
3296           << _("Program ") << ((int) p->patch()->program()) + MIDI_BP_ZERO << '\n' 
3297           << _("Channel ") << ((int) p->patch()->channel() + 1);
3298         show_verbose_cursor (s.str(), 10, 20);
3299         p->item().grab_focus();
3300 }
3301
3302 void
3303 MidiRegionView::patch_left (PatchChange *)
3304 {
3305         trackview.editor().verbose_cursor()->hide ();
3306         /* focus will transfer back via the enter-notify event sent to this
3307          * midi region view.
3308          */
3309 }
3310
3311 void
3312 MidiRegionView::sysex_entered (SysEx* p)
3313 {
3314         ostringstream s;
3315         // CAIROCANVAS
3316         // need a way to extract text from p->_flag->_text
3317         // s << p->text();
3318         // show_verbose_cursor (s.str(), 10, 20);
3319         p->item().grab_focus();
3320 }
3321
3322 void
3323 MidiRegionView::sysex_left (SysEx *)
3324 {
3325         trackview.editor().verbose_cursor()->hide ();
3326         /* focus will transfer back via the enter-notify event sent to this
3327          * midi region view.
3328          */
3329 }
3330
3331 void
3332 MidiRegionView::note_mouse_position (float x_fraction, float /*y_fraction*/, bool can_set_cursor)
3333 {
3334         Editor* editor = dynamic_cast<Editor*>(&trackview.editor());
3335         Editing::MouseMode mm = editor->current_mouse_mode();
3336         bool trimmable = (mm == MouseContent || mm == MouseTimeFX || mm == MouseDraw);
3337
3338         Editor::EnterContext* ctx = editor->get_enter_context(NoteItem);
3339         if (can_set_cursor && ctx) {
3340                 if (trimmable && x_fraction > 0.0 && x_fraction < 0.2) {
3341                         ctx->cursor_ctx->change(editor->cursors()->left_side_trim);
3342                 } else if (trimmable && x_fraction >= 0.8 && x_fraction < 1.0) {
3343                         ctx->cursor_ctx->change(editor->cursors()->right_side_trim);
3344                 } else {
3345                         ctx->cursor_ctx->change(editor->cursors()->grabber_note);
3346                 }
3347         }
3348 }
3349
3350 uint32_t
3351 MidiRegionView::get_fill_color() const
3352 {
3353         const std::string mod_name = (_dragging ? "dragging region" :
3354                                       trackview.editor().internal_editing() ? "editable region" :
3355                                       "midi frame base");
3356         if (_selected) {
3357                 return ARDOUR_UI::config()->color_mod ("selected region base", mod_name);
3358         } else if ((!ARDOUR_UI::config()->get_show_name_highlight() || high_enough_for_name) &&
3359                    !ARDOUR_UI::config()->get_color_regions_using_track_color()) {
3360                 return ARDOUR_UI::config()->color_mod ("midi frame base", mod_name);
3361         }
3362         return ARDOUR_UI::config()->color_mod (fill_color, mod_name);
3363 }
3364
3365 void
3366 MidiRegionView::midi_channel_mode_changed ()
3367 {
3368         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3369         uint16_t mask = mtv->midi_track()->get_playback_channel_mask();
3370         ChannelMode mode = mtv->midi_track()->get_playback_channel_mode ();
3371
3372         if (mode == ForceChannel) {
3373                 mask = 0xFFFF; // Show all notes as active (below)
3374         }
3375
3376         // Update notes for selection
3377         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3378                 (*i)->on_channel_selection_change (mask);
3379         }
3380
3381         _patch_changes.clear ();
3382         display_patch_changes ();
3383 }
3384
3385 void
3386 MidiRegionView::instrument_settings_changed ()
3387 {
3388         redisplay_model();
3389 }
3390
3391 void
3392 MidiRegionView::cut_copy_clear (Editing::CutCopyOp op)
3393 {
3394         if (_selection.empty()) {
3395                 return;
3396         }
3397
3398         PublicEditor& editor (trackview.editor());
3399
3400         switch (op) {
3401         case Delete:
3402                 /* XXX what to do ? */
3403                 break;
3404         case Cut:
3405         case Copy:
3406                 editor.get_cut_buffer().add (selection_as_cut_buffer());
3407                 break;
3408         default:
3409                 break;
3410         }
3411
3412         if (op != Copy) {
3413
3414                 start_note_diff_command();
3415
3416                 for (Selection::iterator i = _selection.begin(); i != _selection.end(); ++i) {
3417                         switch (op) {
3418                         case Copy:
3419                                 break;
3420                         case Delete:
3421                         case Cut:
3422                         case Clear:
3423                                 note_diff_remove_note (*i);
3424                                 break;
3425                         }
3426                 }
3427
3428                 apply_diff();
3429         }
3430 }
3431
3432 MidiCutBuffer*
3433 MidiRegionView::selection_as_cut_buffer () const
3434 {
3435         Notes notes;
3436
3437         for (Selection::const_iterator i = _selection.begin(); i != _selection.end(); ++i) {
3438                 NoteType* n = (*i)->note().get();
3439                 notes.insert (boost::shared_ptr<NoteType> (new NoteType (*n)));
3440         }
3441
3442         MidiCutBuffer* cb = new MidiCutBuffer (trackview.session());
3443         cb->set (notes);
3444
3445         return cb;
3446 }
3447
3448 /** This method handles undo */
3449 bool
3450 MidiRegionView::paste (framepos_t pos, const ::Selection& selection, PasteContext& ctx)
3451 {
3452         bool commit = false;
3453         // Paste notes, if available
3454         MidiNoteSelection::const_iterator m = selection.midi_notes.get_nth(ctx.counts.n_notes());
3455         if (m != selection.midi_notes.end()) {
3456                 ctx.counts.increase_n_notes();
3457                 if (!(*m)->empty()) { commit = true; }
3458                 paste_internal(pos, ctx.count, ctx.times, **m);
3459         }
3460
3461         // Paste control points to automation children, if available
3462         typedef RouteTimeAxisView::AutomationTracks ATracks;
3463         const ATracks& atracks = midi_view()->automation_tracks();
3464         for (ATracks::const_iterator a = atracks.begin(); a != atracks.end(); ++a) {
3465                 if (a->second->paste(pos, selection, ctx)) {
3466                         commit = true;
3467                 }
3468         }
3469
3470         if (commit) {
3471                 trackview.editor().commit_reversible_command ();
3472         }
3473         return true;
3474 }
3475
3476 /** This method handles undo */
3477 void
3478 MidiRegionView::paste_internal (framepos_t pos, unsigned paste_count, float times, const MidiCutBuffer& mcb)
3479 {
3480         if (mcb.empty()) {
3481                 return;
3482         }
3483
3484         start_note_diff_command (_("paste"));
3485
3486         const Evoral::Beats snap_beats    = get_grid_beats(pos);
3487         const Evoral::Beats first_time    = (*mcb.notes().begin())->time();
3488         const Evoral::Beats last_time     = (*mcb.notes().rbegin())->end_time();
3489         const Evoral::Beats duration      = last_time - first_time;
3490         const Evoral::Beats snap_duration = duration.snap_to(snap_beats);
3491         const Evoral::Beats paste_offset  = snap_duration * paste_count;
3492         const Evoral::Beats pos_beats     = absolute_frames_to_source_beats(pos) + paste_offset;
3493         Evoral::Beats       end_point     = Evoral::Beats();
3494
3495         DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste data spans from %1 to %2 (%3) ; paste pos beats = %4 (based on %5 - %6)\n",
3496                                                        first_time,
3497                                                        last_time,
3498                                                        duration, pos, _region->position(),
3499                                                        pos_beats));
3500
3501         clear_selection ();
3502
3503         for (int n = 0; n < (int) times; ++n) {
3504
3505                 for (Notes::const_iterator i = mcb.notes().begin(); i != mcb.notes().end(); ++i) {
3506
3507                         boost::shared_ptr<NoteType> copied_note (new NoteType (*((*i).get())));
3508                         copied_note->set_time (pos_beats + copied_note->time() - first_time);
3509
3510                         /* make all newly added notes selected */
3511
3512                         note_diff_add_note (copied_note, true);
3513                         end_point = copied_note->end_time();
3514                 }
3515         }
3516
3517         /* if we pasted past the current end of the region, extend the region */
3518
3519         framepos_t end_frame = source_beats_to_absolute_frames (end_point);
3520         framepos_t region_end = _region->position() + _region->length() - 1;
3521
3522         if (end_frame > region_end) {
3523
3524                 DEBUG_TRACE (DEBUG::CutNPaste, string_compose ("Paste extended region from %1 to %2\n", region_end, end_frame));
3525
3526                 _region->clear_changes ();
3527                 _region->set_length (end_frame - _region->position());
3528                 trackview.session()->add_command (new StatefulDiffCommand (_region));
3529         }
3530
3531         apply_diff (true);
3532 }
3533
3534 struct EventNoteTimeEarlyFirstComparator {
3535         bool operator() (NoteBase* a, NoteBase* b) {
3536                 return a->note()->time() < b->note()->time();
3537         }
3538 };
3539
3540 void
3541 MidiRegionView::time_sort_events ()
3542 {
3543         if (!_sort_needed) {
3544                 return;
3545         }
3546
3547         EventNoteTimeEarlyFirstComparator cmp;
3548         _events.sort (cmp);
3549
3550         _sort_needed = false;
3551 }
3552
3553 void
3554 MidiRegionView::goto_next_note (bool add_to_selection)
3555 {
3556         bool use_next = false;
3557
3558         if (_events.back()->selected()) {
3559                 return;
3560         }
3561
3562         time_sort_events ();
3563
3564         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3565         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask();
3566
3567         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3568                 if ((*i)->selected()) {
3569                         use_next = true;
3570                         continue;
3571                 } else if (use_next) {
3572                         if (channel_mask & (1 << (*i)->note()->channel())) {
3573                                 if (!add_to_selection) {
3574                                         unique_select (*i);
3575                                 } else {
3576                                         note_selected (*i, true, false);
3577                                 }
3578                                 return;
3579                         }
3580                 }
3581         }
3582
3583         /* use the first one */
3584
3585         if (!_events.empty() && (channel_mask & (1 << _events.front()->note()->channel ()))) {
3586                 unique_select (_events.front());
3587         }
3588 }
3589
3590 void
3591 MidiRegionView::goto_previous_note (bool add_to_selection)
3592 {
3593         bool use_next = false;
3594
3595         if (_events.front()->selected()) {
3596                 return;
3597         }
3598
3599         time_sort_events ();
3600
3601         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3602         uint16_t const channel_mask = mtv->midi_track()->get_playback_channel_mask ();
3603
3604         for (Events::reverse_iterator i = _events.rbegin(); i != _events.rend(); ++i) {
3605                 if ((*i)->selected()) {
3606                         use_next = true;
3607                         continue;
3608                 } else if (use_next) {
3609                         if (channel_mask & (1 << (*i)->note()->channel())) {
3610                                 if (!add_to_selection) {
3611                                         unique_select (*i);
3612                                 } else {
3613                                         note_selected (*i, true, false);
3614                                 }
3615                                 return;
3616                         }
3617                 }
3618         }
3619
3620         /* use the last one */
3621
3622         if (!_events.empty() && (channel_mask & (1 << (*_events.rbegin())->note()->channel ()))) {
3623                 unique_select (*(_events.rbegin()));
3624         }
3625 }
3626
3627 void
3628 MidiRegionView::selection_as_notelist (Notes& selected, bool allow_all_if_none_selected)
3629 {
3630         bool had_selected = false;
3631
3632         time_sort_events ();
3633
3634         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3635                 if ((*i)->selected()) {
3636                         selected.insert ((*i)->note());
3637                         had_selected = true;
3638                 }
3639         }
3640
3641         if (allow_all_if_none_selected && !had_selected) {
3642                 for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3643                         selected.insert ((*i)->note());
3644                 }
3645         }
3646 }
3647
3648 void
3649 MidiRegionView::update_ghost_note (double x, double y)
3650 {
3651         x = std::max(0.0, x);
3652
3653         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3654
3655         _last_ghost_x = x;
3656         _last_ghost_y = y;
3657
3658         _note_group->canvas_to_item (x, y);
3659
3660         PublicEditor& editor = trackview.editor ();
3661         
3662         framepos_t const unsnapped_frame = editor.pixel_to_sample (x);
3663         framecnt_t grid_frames;
3664         framepos_t const f = snap_frame_to_grid_underneath (unsnapped_frame, grid_frames);
3665
3666         /* calculate time in beats relative to start of source */
3667         const Evoral::Beats length = get_grid_beats(unsnapped_frame);
3668         const Evoral::Beats time   = std::max(
3669                 Evoral::Beats(),
3670                 absolute_frames_to_source_beats (f + _region->position ()));
3671
3672         _ghost_note->note()->set_time (time);
3673         _ghost_note->note()->set_length (length);
3674         _ghost_note->note()->set_note (midi_stream_view()->y_to_note (y));
3675         _ghost_note->note()->set_channel (mtv->get_channel_for_add ());
3676         _ghost_note->note()->set_velocity (get_velocity_for_add (time));
3677
3678         /* the ghost note does not appear in ghost regions, so pass false in here */
3679         update_note (_ghost_note, false);
3680
3681         show_verbose_cursor (_ghost_note->note ());
3682 }
3683
3684 void
3685 MidiRegionView::create_ghost_note (double x, double y)
3686 {
3687         remove_ghost_note ();
3688
3689         boost::shared_ptr<NoteType> g (new NoteType);
3690         if (midi_view()->note_mode() == Sustained) {
3691                 _ghost_note = new Note (*this, _note_group, g);
3692         } else {
3693                 _ghost_note = new Hit (*this, _note_group, 10, g);
3694         }
3695         _ghost_note->set_ignore_events (true);
3696         _ghost_note->set_outline_color (0x000000aa);
3697         update_ghost_note (x, y);
3698         _ghost_note->show ();
3699
3700         show_verbose_cursor (_ghost_note->note ());
3701 }
3702
3703 void
3704 MidiRegionView::remove_ghost_note ()
3705 {
3706         delete _ghost_note;
3707         _ghost_note = 0;
3708 }
3709
3710 void
3711 MidiRegionView::snap_changed ()
3712 {
3713         if (!_ghost_note) {
3714                 return;
3715         }
3716
3717         create_ghost_note (_last_ghost_x, _last_ghost_y);
3718 }
3719
3720 void
3721 MidiRegionView::drop_down_keys ()
3722 {
3723         _mouse_state = None;
3724 }
3725
3726 void
3727 MidiRegionView::maybe_select_by_position (GdkEventButton* ev, double /*x*/, double y)
3728 {
3729         /* XXX: This is dead code.  What was it for? */
3730
3731         double note = midi_stream_view()->y_to_note(y);
3732         Events e;
3733         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3734
3735         uint16_t chn_mask = mtv->midi_track()->get_playback_channel_mask();
3736
3737         if (Keyboard::modifier_state_equals (ev->state, Keyboard::TertiaryModifier)) {
3738                 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchGreaterThanOrEqual, (uint8_t) floor (note), chn_mask);
3739         } else if (Keyboard::modifier_state_equals (ev->state, Keyboard::PrimaryModifier)) {
3740                 get_events (e, Evoral::Sequence<Evoral::Beats>::PitchLessThanOrEqual, (uint8_t) floor (note), chn_mask);
3741         } else {
3742                 return;
3743         }
3744
3745         bool add_mrv_selection = false;
3746
3747         if (_selection.empty()) {
3748                 add_mrv_selection = true;
3749         }
3750
3751         for (Events::iterator i = e.begin(); i != e.end(); ++i) {
3752                 if (_selection.insert (*i).second) {
3753                         (*i)->set_selected (true);
3754                 }
3755         }
3756
3757         if (add_mrv_selection) {
3758                 PublicEditor& editor (trackview.editor());
3759                 editor.get_selection().add (this);
3760         }
3761 }
3762
3763 void
3764 MidiRegionView::color_handler ()
3765 {
3766         RegionView::color_handler ();
3767
3768         for (Events::iterator i = _events.begin(); i != _events.end(); ++i) {
3769                 (*i)->set_selected ((*i)->selected()); // will change color
3770         }
3771
3772         /* XXX probably more to do here */
3773 }
3774
3775 void
3776 MidiRegionView::enable_display (bool yn)
3777 {
3778         RegionView::enable_display (yn);
3779         if (yn) {
3780                 redisplay_model ();
3781         }
3782 }
3783
3784 void
3785 MidiRegionView::show_step_edit_cursor (Evoral::Beats pos)
3786 {
3787         if (_step_edit_cursor == 0) {
3788                 ArdourCanvas::Item* const group = get_canvas_group();
3789
3790                 _step_edit_cursor = new ArdourCanvas::Rectangle (group);
3791                 _step_edit_cursor->set_y0 (0);
3792                 _step_edit_cursor->set_y1 (midi_stream_view()->contents_height());
3793                 _step_edit_cursor->set_fill_color (RGBA_TO_UINT (45,0,0,90));
3794                 _step_edit_cursor->set_outline_color (RGBA_TO_UINT (85,0,0,90));
3795         }
3796
3797         move_step_edit_cursor (pos);
3798         _step_edit_cursor->show ();
3799 }
3800
3801 void
3802 MidiRegionView::move_step_edit_cursor (Evoral::Beats pos)
3803 {
3804         _step_edit_cursor_position = pos;
3805
3806         if (_step_edit_cursor) {
3807                 double pixel = trackview.editor().sample_to_pixel (region_beats_to_region_frames (pos));
3808                 _step_edit_cursor->set_x0 (pixel);
3809                 set_step_edit_cursor_width (_step_edit_cursor_width);
3810         }
3811 }
3812
3813 void
3814 MidiRegionView::hide_step_edit_cursor ()
3815 {
3816         if (_step_edit_cursor) {
3817                 _step_edit_cursor->hide ();
3818         }
3819 }
3820
3821 void
3822 MidiRegionView::set_step_edit_cursor_width (Evoral::Beats beats)
3823 {
3824         _step_edit_cursor_width = beats;
3825
3826         if (_step_edit_cursor) {
3827                 _step_edit_cursor->set_x1 (_step_edit_cursor->x0() + trackview.editor().sample_to_pixel (region_beats_to_region_frames (beats)));
3828         }
3829 }
3830
3831 /** Called when a diskstream on our track has received some data.  Update the view, if applicable.
3832  *  @param w Source that the data will end up in.
3833  */
3834 void
3835 MidiRegionView::data_recorded (boost::weak_ptr<MidiSource> w)
3836 {
3837         if (!_active_notes) {
3838                 /* we aren't actively being recorded to */
3839                 return;
3840         }
3841
3842         boost::shared_ptr<MidiSource> src = w.lock ();
3843         if (!src || src != midi_region()->midi_source()) {
3844                 /* recorded data was not destined for our source */
3845                 return;
3846         }
3847
3848         MidiTimeAxisView* mtv = dynamic_cast<MidiTimeAxisView*> (&trackview);
3849
3850         boost::shared_ptr<MidiBuffer> buf = mtv->midi_track()->get_gui_feed_buffer ();
3851
3852         framepos_t back = max_framepos;
3853
3854         for (MidiBuffer::iterator i = buf->begin(); i != buf->end(); ++i) {
3855                 Evoral::MIDIEvent<MidiBuffer::TimeType> const ev (*i, false);
3856
3857                 if (ev.is_channel_event()) {
3858                         if (get_channel_mode() == FilterChannels) {
3859                                 if (((uint16_t(1) << ev.channel()) & get_selected_channels()) == 0) {
3860                                         continue;
3861                                 }
3862                         }
3863                 }
3864
3865                 /* convert from session frames to source beats */
3866                 Evoral::Beats const time_beats = _source_relative_time_converter.from(
3867                         ev.time() - src->timeline_position() + _region->start());
3868
3869                 if (ev.type() == MIDI_CMD_NOTE_ON) {
3870                         boost::shared_ptr<NoteType> note (
3871                                 new NoteType (ev.channel(), time_beats, Evoral::Beats(), ev.note(), ev.velocity()));
3872
3873                         add_note (note, true);
3874
3875                         /* fix up our note range */
3876                         if (ev.note() < _current_range_min) {
3877                                 midi_stream_view()->apply_note_range (ev.note(), _current_range_max, true);
3878                         } else if (ev.note() > _current_range_max) {
3879                                 midi_stream_view()->apply_note_range (_current_range_min, ev.note(), true);
3880                         }
3881
3882                 } else if (ev.type() == MIDI_CMD_NOTE_OFF) {
3883                         resolve_note (ev.note (), time_beats);
3884                 }
3885
3886                 back = ev.time ();
3887         }
3888
3889         midi_stream_view()->check_record_layers (region(), back);
3890 }
3891
3892 void
3893 MidiRegionView::trim_front_starting ()
3894 {
3895         /* Reparent the note group to the region view's parent, so that it doesn't change
3896            when the region view is trimmed.
3897         */
3898         _temporary_note_group = new ArdourCanvas::Container (group->parent ());
3899         _temporary_note_group->move (group->position ());
3900         _note_group->reparent (_temporary_note_group);
3901 }
3902
3903 void
3904 MidiRegionView::trim_front_ending ()
3905 {
3906         _note_group->reparent (group);
3907         delete _temporary_note_group;
3908         _temporary_note_group = 0;
3909
3910         if (_region->start() < 0) {
3911                 /* Trim drag made start time -ve; fix this */
3912                 midi_region()->fix_negative_start ();
3913         }
3914 }
3915
3916 void
3917 MidiRegionView::edit_patch_change (PatchChange* pc)
3918 {
3919         PatchChangeDialog d (&_source_relative_time_converter, trackview.session(), *pc->patch (), instrument_info(), Gtk::Stock::APPLY, true);
3920
3921         int response = d.run();
3922
3923         switch (response) {
3924         case Gtk::RESPONSE_ACCEPT:
3925                 break;
3926         case Gtk::RESPONSE_REJECT:
3927                 delete_patch_change (pc);
3928                 return;
3929         default:
3930                 return;
3931         }
3932
3933         change_patch_change (pc->patch(), d.patch ());
3934 }
3935
3936 void
3937 MidiRegionView::delete_sysex (SysEx* /*sysex*/)
3938 {
3939         // CAIROCANVAS
3940         // sysyex object doesn't have a pointer to a sysex event
3941         // MidiModel::SysExDiffCommand* c = _model->new_sysex_diff_command (_("delete sysex"));
3942         // c->remove (sysex->sysex());
3943         // _model->apply_command (*trackview.session(), c);
3944
3945         //_sys_exes.clear ();
3946         // display_sysexes();
3947 }
3948
3949 void
3950 MidiRegionView::show_verbose_cursor (boost::shared_ptr<NoteType> n) const
3951 {
3952         using namespace MIDI::Name;
3953
3954         std::string name;
3955
3956         MidiTimeAxisView* const mtv = dynamic_cast<MidiTimeAxisView*>(&trackview);
3957         if (mtv) {
3958                 boost::shared_ptr<MasterDeviceNames> device_names(mtv->get_device_names());
3959                 if (device_names) {
3960                         MIDI::Name::PatchPrimaryKey patch_key;
3961                         get_patch_key_at(n->time(), n->channel(), patch_key);
3962                         name = device_names->note_name(mtv->gui_property(X_("midnam-custom-device-mode")),
3963                                                        n->channel(),
3964                                                        patch_key.bank(),
3965                                                        patch_key.program(),
3966                                                        n->note());
3967                 }
3968         }
3969
3970         char buf[128];
3971         snprintf (buf, sizeof (buf), "%d %s\nCh %d Vel %d",
3972                   (int) n->note (),
3973                   name.empty() ? Evoral::midi_note_name (n->note()).c_str() : name.c_str(),
3974                   (int) n->channel() + 1,
3975                   (int) n->velocity());
3976
3977         show_verbose_cursor(buf, 10, 20);
3978 }
3979
3980 void
3981 MidiRegionView::show_verbose_cursor (string const & text, double xoffset, double yoffset) const
3982 {
3983         trackview.editor().verbose_cursor()->set (text);
3984         trackview.editor().verbose_cursor()->show ();
3985         trackview.editor().verbose_cursor()->set_offset (ArdourCanvas::Duple (xoffset, yoffset));
3986 }
3987
3988 uint8_t
3989 MidiRegionView::get_velocity_for_add (MidiModel::TimeType time) const
3990 {
3991         if (_model->notes().empty()) {
3992                 return 0x40;  // No notes, use default
3993         }
3994
3995         MidiModel::Notes::const_iterator m = _model->note_lower_bound(time);
3996         if (m == _model->notes().begin()) {
3997                 // Before the start, use the velocity of the first note
3998                 return (*m)->velocity();
3999         } else if (m == _model->notes().end()) {
4000                 // Past the end, use the velocity of the last note
4001                 --m;
4002                 return (*m)->velocity();
4003         }
4004
4005         // Interpolate velocity of surrounding notes
4006         MidiModel::Notes::const_iterator n = m;
4007         --n;
4008
4009         const double frac = ((time - (*n)->time()).to_double() /
4010                              ((*m)->time() - (*n)->time()).to_double());
4011
4012         return (*n)->velocity() + (frac * ((*m)->velocity() - (*n)->velocity()));
4013 }
4014
4015 /** @param p A session framepos.
4016  *  @param grid_frames Filled in with the number of frames that a grid interval is at p.
4017  *  @return p snapped to the grid subdivision underneath it.
4018  */
4019 framepos_t
4020 MidiRegionView::snap_frame_to_grid_underneath (framepos_t p, framecnt_t& grid_frames) const
4021 {
4022         PublicEditor& editor = trackview.editor ();
4023         
4024         const Evoral::Beats grid_beats = get_grid_beats(p);
4025
4026         grid_frames = region_beats_to_region_frames (grid_beats);
4027
4028         /* Hack so that we always snap to the note that we are over, instead of snapping
4029            to the next one if we're more than halfway through the one we're over.
4030         */
4031         if (editor.snap_mode() == SnapNormal && p >= grid_frames / 2) {
4032                 p -= grid_frames / 2;
4033         }
4034
4035         return snap_frame_to_frame (p);
4036 }
4037
4038 /** Called when the selection has been cleared in any MidiRegionView.
4039  *  @param rv MidiRegionView that the selection was cleared in.
4040  */
4041 void
4042 MidiRegionView::selection_cleared (MidiRegionView* rv)
4043 {
4044         if (rv == this) {
4045                 return;
4046         }
4047
4048         /* Clear our selection in sympathy; but don't signal the fact */
4049         clear_selection (false);
4050 }
4051
4052 ChannelMode
4053 MidiRegionView::get_channel_mode () const
4054 {
4055         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4056         return rtav->midi_track()->get_playback_channel_mode();
4057 }
4058
4059 uint16_t
4060 MidiRegionView::get_selected_channels () const
4061 {
4062         RouteTimeAxisView* rtav = dynamic_cast<RouteTimeAxisView*> (&trackview);
4063         return rtav->midi_track()->get_playback_channel_mask();
4064 }
4065
4066
4067 Evoral::Beats
4068 MidiRegionView::get_grid_beats(framepos_t pos) const
4069 {
4070         PublicEditor& editor  = trackview.editor();
4071         bool          success = false;
4072         Evoral::Beats beats   = editor.get_grid_type_as_beats(success, pos);
4073         if (!success) {
4074                 beats = Evoral::Beats(1);
4075         }
4076         return beats;
4077 }